Opened 13 years ago
Closed 9 years ago
#17914 closed Bug (wontfix)
Add support for namespaced view references when passing a callable to reverse()
Reported by: | Owned by: | nobody | |
---|---|---|---|
Component: | Core (URLs) | Version: | dev |
Severity: | Normal | Keywords: | |
Cc: | bradley.ayers@…, amirouche.boubekki@…, gwahl@…, Florian Apolloner | Triage Stage: | Accepted |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
It's not possible to reverse a URL by passing a function reference to reverse()
that's hooked into the URLconf under a namespace.
Consider the trivial Django project (that doesn't work):
# urls.py from django.conf.urls.defaults import patterns, include from django.core.urlresolvers import reverse from django.http import HttpResponse def view(request): # Return the URL to this view return HttpResponse(reverse(view)) level2 = patterns('', (r'^2/', view) ) urlpatterns = patterns('', (r'^1/', include(level2, namespace="foo")) )
Removing , namespace="foo"
will make it work.
My understanding of why this happens, is that reverse()
traverses RegexURLResolver
s
*only* for strings. Function references are passed directly to the root RegexURLResolver.reverse
method.:
# from django/core/urlresolvers.py ~ line 432 if not isinstance(viewname, basestring): view = viewname else: # <snip> -- resolver traversal happens here to honor namespaces # `resolver` is reassigned to last RegexURLResolver in namespace chain return iri_to_uri(resolver._reverse_with_prefix(view, prefix, *args, **kwargs))
Obviously traversing namespaces isn't possible for a function reference,
so what's needed is to add *all* descendent view references into each RegexURLResolver
's
reverse_dict
dictionary.
Populating reverse_dict
is lazy and happens on first access, the work is
performed in RegexURLResolver._populate
:
# django/core/urlresolvers.py ~ line 237 def _populate(self): # <snip> for pattern in reversed(self.url_patterns): # <snip> if isinstance(pattern, RegexURLResolver): if pattern.namespace: # <snip> -- tldr `self.reverse_dict` isn't populated, `pattern` (the # RegexURLResolver) is instead stored in `self.namespace_dict` else: # <snip> -- tldr `self.reverse_dict` *is* populated with the keys # being the name of each URL (or the function reference) # defined in this level. (i.e. only children, not all descendants) else: # <snip> -- tldr `self.reverse_dict` is again populated
This combination of behaviour by RegexURLResolver._populate
and reverse()
leads to the problem.
Attachments (3)
Change History (26)
by , 13 years ago
Attachment: | 17914-test.patch added |
---|
comment:1 by , 13 years ago
Has patch: | set |
---|
comment:2 by , 13 years ago
Cc: | added |
---|
comment:3 by , 13 years ago
Version: | 1.3 → SVN |
---|
comment:4 by , 13 years ago
It should be noted that this patch is technically backwards incompatible. After applying this patch, it's no longer possible to use recursive URL patterns, e.g. consider a project named 'foo', and it had a urls.py
that contained:
urlpatterns = patterns('', (r'^abc/', include('foo.urls'))
comment:5 by , 13 years ago
Patch needs improvement: | set |
---|---|
Triage Stage: | Unreviewed → Accepted |
Accepting.
Like we talked about on IRC, let's just avoid circular imports when doing your traversal rather than dropping that functionality.
comment:6 by , 12 years ago
I've updated the patch to maintain support for recursive urlconfs. https://github.com/django/django/pull/174
comment:7 by , 12 years ago
Doing a quick scan of URL reversing during a sprint, the last git code looks reasonable. Not ready to commit yet -- I haven't fully digested it, but my intuition is that it's the right kind of thing.
Except(!) that using function objects in reverse() situations is kind of a "don't do that" kind of situation. We permitted it initially for smooth integration, but it's kind of a lucky happenstance. My point being, that a patch of this size is a reasonable to fix this ticket. If it grows to twice this size, we should maybe just add a "don't do that" note to the documentation -- the extra complexity isn't worth it.
For now, charge ahead!
comment:8 by , 12 years ago
Cc: | added |
---|
comment:9 by , 12 years ago
Cc: | added |
---|
comment:11 by , 11 years ago
Any news here?
Since reverse now accepts app_name -- as well as include -- we could easily find corresponding namespace. E.g.:
from django.conf.urls import * urlpatterns = patterns('', url(r'^payment/', include('payment.urls', namespace='payment', app_name='payment')), ) reverse(view, current_app='payment') # try with current_app given
As alternative solution -- we could require passing namespace when using with function. The we could convert function to its name, add namespace+":" and use existing machinery.
comment:12 by , 11 years ago
#21899 was closed a duplicate of this ticket, but I'm not sure they are the same thing. Will fixing this ticket also fix #21899? This ticket seems to be about reversing raw callable view functions, for which we have no way to specify a namespace. The other ticket was about reversing a dotted path reference to a view function, specified as a string with a namespace.
follow-up: 15 comment:13 by , 10 years ago
Cc: | added |
---|
Can someone present a usecase for using function references in reverse? IMO this should just get closed as wontfix and names should be used throughout instead.
comment:14 by , 10 years ago
This feels like using args and kwargs on reverses. Using either function reverses or namespaces is fine, just don't mix them.
Maybe we should enforce it a bit more like we do with reversing with args and kwargs?
follow-up: 16 comment:15 by , 9 years ago
Replying to apollo13:
Can someone present a usecase for using function references in reverse? IMO this should just get closed as wontfix and names should be used throughout instead.
For me the view-name is redundant. We try to avoid it. Reversing by function reference works fine (except this bug). Jumping through the code with an IDE is much more fun if you use function references.
It would be nice if this could be solved. For our team this means: don't use namespaced URLs :-(
comment:16 by , 9 years ago
Replying to guettli:
For me the view-name is redundant. We try to avoid it. Reversing by function reference works fine (except this bug). Jumping through the code with an IDE is much more fun if you use function references.
If I remember correctly we've been over the redundancy point on the ML already, you can always just write your own url function which sets the path of the function as name, not really redundant imo. Also reversing by function reference is something which is ugly in templates etc imo… And redundant is relative anyways, "app:view_name" is imo way nicer than "app.views.view_name" -- especially if you have nested imports.
comment:17 by , 9 years ago
I'm in favor of a "won't fix" of this issue and promoting reversing by URL name as Florian suggested. We could deprecate passing callables to reverse, but this will probably just needlessly annoy people who are happy with it. Instead, we could just remove it from the docs like we did with the @permalink
decorator.
comment:18 by , 9 years ago
Summary: | reverse() does not support namespaced view references → reverse() does not support namespaced view references when passed a callable |
---|
by , 9 years ago
Attachment: | 17914.diff added |
---|
comment:19 by , 9 years ago
Patch needs improvement: | unset |
---|
Added a documentation patch to discourage reversing by callable view.
comment:23 by , 9 years ago
Resolution: | → wontfix |
---|---|
Status: | new → closed |
Summary: | reverse() does not support namespaced view references when passed a callable → Add support for namespaced view references when passing a callable to reverse() |
test to expose bug