Django: Reverse URL lookup with arbitrary URL components - django

Assume a Django app, shop, whose urls.py is included in the main urls.py:
# main urls.py
urlpatterns = patterns('',
(r'^[^/]+/shop/', include('shop.urls')),
)
Note, that there is an arbitrary prefix before /shop. This is of no interest for the shop app, it's only interesting to some middleware.
The shop.urls could look like this:
# shop's urls.py
urlpatterns = patterns('',
url(r'^$', index, name="shop_index"),
url(r'^product/(?P<id>[^/]+)$', product, name="shop_product"),
)
I now want to use {% url %} in my shop templates. What overthrows this plan is, that the generated URL doesn't include the prefix from the global urls.py. The generated URLs look more like, e.g.
/^/shop/product/1
Question: Now, is there a possibility (in Django 1.2 or svn trunk) to enable the reverse lookup to use just the same prefix, that is there in the current URL? For example, if request.path is /foo/shop/, that {% urls shop_product id=1 %} returns
/foo/shop/product/1
The only way I came up with is to embrace the prefix and send it to the shop app views, but this is a really bad solution, since shop then has to deal with something it doesn't need at all.

No there is not, not a straightforward django way anyway. Django has no idea of knowing what you mean with [^/]+, in your case that it is a prefix that should be dynamically be added to a reverse url lookup.
I wonder also why you add middleware specific info to an url and not even as GET paramters, doing something like this is jsut asking for trouble imho. If you say the prefix is middleware-specific, it doesn't make sense to mess with it somewhere outside of the middleware.
A solution that could work (did not test it) is setting in your middleware a request context variable like environment_prefix, then append this manually before the url. So something like this:
/{{ environment_prefix }}{% filter slice:"1:" %}{% url shopview %}{% endfilter %}
Another possiblity is to try to implemented your own url template tag (inheritted from the url templatetag) to always include the current prefix which can again be a context variable set by the middleware.

Related

Django Nested URL passing slugs

I'm looking to accomplish a URL as so:
/group/<group_name>/discussion/<discussion_name>/
My implementation is shown below
project urls.py
url(r'^group/', include('groups.urls')
group urls.py
url(r'^(?P<gslug>[\w-]+)/discussion/', include('discussions.urls')),
discussion urls.py
url(r'^(?P<slug>[\w-]+)',views.discussion_detail, name='discussion_detail'))
Unfortunately in my views.discussion_detail I do not have access to both gslug and slug. Where have I gone wrong?
discussion views.py
def discussion_detail(request, gslug, slug):
pass //logic in here
Make sure that you're passing variables into the view properly with the 'url' template tag like so
{% url 'discussion_detail' group.slug discussion.slug %}
Let me know if that works :)

NoReverseMatch at /, u'opts|admin_urlname' is not a registered namespace Django 1.4.1

Django newbie here. Following the documentation, I am trying the following to get a link to the admin site from the homepage of the public site I'm building:
{% load admin_urls %}
<p>Go to the admin.</p>
I am getting the error:
NoReverseMatch at /
u'opts|admin_urlname' is not a registered namespace
I am including the URLs properly:
url(r'^admin/', include(admin.site.urls)),
My template loaders are in the right order.
I've tried a few different variations on this, and they all throw namespace errors.
Any ideas? Thanks!
After 30 minutes with Daniel Roseman / Django docs in one screen and my code in the other, I come up with this simple solution:
In your views.py, add the opts context with the _meta of the model (that includes the required app_label and model_name):
class YourModelDetailView(DetailView):
def get_context_data(self, **kwargs):
context = super(YourModelDetailView, self).get_context_data(**kwargs)
context["opts"] = YourModel._meta
return context
In your templates:
{% url opts|admin_urlname:'change' object.pk %}
Where change can be any action in the reverse admin urls documentation page.
While the above answers were helpful about the code I was calling, there is a much easier way. I'm using this instead:
{% url 'admin:index' %}
This works for custom admin views as well, like:
{% url 'admin:myapp_mymodel_<keyword>' object.id %}
Where keyword is from the named parameters listed here (i.e. add, change, delete).
You are almost certainly using the released 1.4 version, rather than the development version. As the documentation for that version shows, you need to use {% load url from future %} before you can use that syntax.

How do a specify get_absolute_url() on a view when the model isn't clear?

I have a place in my Django app where I need to construct a callback to my domain after a third-party auth, but I'm stuck on how to do this since that view in question doesn't really map to one model (or rather, the view code references multiple models), and the docs for get_absolute_url() construction and permalinks all reference models.
For instance, in my template I currently have something like:
<a class="btn btn-danger large" href="http://to/third/party?api_key=noneyobiz&cb=http://localhost:8000/signup">Join via Somethingorother</a>
the line for this view in urls.py is:
url(r'^signup/$', 'signup', name="signup"),
I want the hardcoded 'http://localhost:8000/signup' to be dynamic. I'm hoping this functionality doesn't depend on my using generic views. Actually I don't understand why generating a permalink is even tied to models at all, it seems like it should only depend on the urlconf. What am I missing here?
permalink is only for the use case when you are referencing a model directly. To find a non-model-based URL, you can use the url tag - in your case, {% url signup %}.
permalink is a thin wrapper of django.core.urlresolvers.reverse. Its belongs to django.db.models to be a shortcut because we usually write reverse inside get_absolute_url of models. So use reverse here
from django.core.urlresolvers import reverse
path = reverse('signup')
Update
To use absolute URI, you could
hardcode in settings or use something like Site.objects.get_current() w/ the path you get from reverse or url to get the absolute URI, as Daniel suggested.
If your callback URI is in the same domain w/ the view rendering the template, you could rely on request to get actual absolute URI:
request.build_absolute_uri(reverse('signup'))
Furthermore, you may want to escape the URI in template, like {{ absolute_uri|urlencode }}. or in view through urllib.quote or urllib.urlencode

Add links to navigation based on INSTALLED_APPS

I have a project that has a number of apps. These apps translate into modules that perform different functions for the end user.
Now each deployment of the project may have certain apps enabled or disabled.
What I'd like to achieve is a navigation list of links that only displays links for apps that are in INSTALLED_APPS.
For example deployment 1 has App1 and App2 listed in INSTALLED_APPS so would have a navigation something like this:
Link to App1's view
Link to App2's view
And deployment 2 has App2 and App3 installed so should show a navigation something like this:
Link to App2's view
Link to App3's view
Without having a navigation defined in a base template and editing it for each deployment, I can't see a way of doing this. Even by using { block.super }, this seems to not allow 2 apps to be installed as each child template would append to the parent.
This is strictly from the top of my head, so it may not be the "best" or most appropriate way.
First, if you're going to rely on INSTALLED_APPS you should actually implement something along the lines of:
MY_INSTALLED_APPS = (
'app1',
'app2',
)
INSTALLED_APPS = (
# other installed apps
) + MY_INSTALLED_APPS
Then, instead of having to figure out which of the items in INSTALLED_APPS are yours and which are third-party, you just use MY_INSTALLED_APPS instead for things like building your menus.
Second, in Django, apps aren't tied to views in any meaningful way. There's no concept of a "default" view, and you can't simply link to an app. However, you can some what achieve this idea through the use of namespaces and a convention for view naming, specifically, all your apps will at least have a urlpattern named "index", for example.
Then, in your urls.py you create urlpatterns like:
url(r'^app1/', include('app1.urls', namespace='app1', app_name='app1'),
url(r'^app2/', include('app2.urls', namespace='app2', app_name='app2'),
# etc
In each app, you create a urls.py that has at least one urlpattern:
url(r'^$', some_view, name='index'),
This means that the going to /app1/ in your browser would then load some_view and you can reference this view in your code with a name like: app1:index.
The tricky part is using this in your templates. Django 1.5 will add the ability to use context variables in the {% url %) tag for the view name. As of Django 1.3, you can use this behavior as well via {% load url from future %}. However, even that only gets you part-way to what you need.
In your template, you'll need to loop through the values of MY_INSTALLED_APPS and construct the links. The following should work in Django 1.3-1.4 via {% load url from future %} or Django 1.5:
{% for app in apps %}
{{ app }}
{% endfor %}
The other way to accomplish this is with a template filter, which is your only option in Django <1.3, and may still be preferable in later versions. Something like:
#register.filter
def default_url_for_app(app):
return reverse(app+':index')
And, in your template:
{% for app in apps %}
{{ app }}
{% endfor %}
You can simple import your settings.py file and iterate over the INSTALLED_APPS list.
from django.conf import settings
for installed_app in settings.INSTALLED_APPS:
But I'm also in doubt if this is a smart decision to implement your application as such.
What you really want here is some kind of app registration system. That is, for each app that's loaded (ie included in INSTALLED_APPS), register its homepage with the list of links.
Rather than doing all this automatically, I would follow the example of the admin application and get each app to call a register function on first load. You could do this in the models.py, for example, since you know that will be imported by Django on startup. The register function would take the app name and the url and build a dictionary which could then be passed to each template via a context processor.

Reversible, named URLs for Django flatpages (or multilingual-ng)?

Is there a way to have reversible, named URLs for Django flatpages (or multilingual-ng, which is flatpages + translations)?
If not, is there a similar app available which can have named URLs for pages which are editable through the admin? (And I'd rather avoid any behemoth CMSs, please.)
edit: Please see the comments in the answers below for more discussion and clarification of the question.
As the very last thing in your root URLConf, put
(r'^(?P<url>.*)$', 'django.contrib.flatpages.views.flatpage'),
Now you can use the {% url %} tag, with the view name flatpage and the keyword argument url. You can also use the reverse() function in views.
{% url %} template tag works with URLconf, whereas flatpages are handling nonexistent urls.
If you really want to reverse flatpages into URLs, my guess is that you have to write your own {% flatpage_url %} tag, which would looks like this:
#register.simple_tag
def flatpage_url(self, name):
return FlatPage.objects.get({param}=name).url
-- where {param} is one of FlatPage model fields.
Also, you can merge {% url %} and {% flatpageurl %} tags together, so that the latter fallbacks to reverse() in case FlatPage is not found (and reverse() is what {% url %} uses).
EDIT: I don't accept any more upvotes as James Bennet's answer is The One for this question (and I'm ashamed I overlooked such trivial solution) - so please upvote it.
Aren't the proposed solutions basically the same as hardcoding the URL in the template? What's the difference between hardcoding the URL in href="URL" vs. doing it in {% url URL %}?
I think a better solution is found on: How can I get the reverse url for a Django Flatpages template, proposed by bufh.
I think the optimal way of achieving this would be adding an extra 'name' field on the FlatPage model.