Adrian, On Tue, 2007-03-27 at 16:57 -0500, Adrian Holovaty wrote: [...] > I've done some thinking on this, too, and I think the cleanest way to > solve it would be to introduce optional names for URL patterns. > Something like this: > > url(r'^objects/$', some_view, name='object_view'), > url(r'^objects2/$', some_view, name='object_view2'), > > This way, you could give an arbitrary name to a particular pattern, so > that you could target it via a reverse lookup. The Routes package does > something similar ("named routes"). Note that it would require URL > patterns to be function calls -- url() -- but that's probably a good > long-term switch, anyway, because it lets us do more interesting > things. We could likely keep it backwards compatible by changing the > patterns() function to convert any plain tuple into an object of the > type url() returns.
Are you happy with the url template tag being a little bit flexible in how it does the matching? I mean, rather than having to specify whether you are giving the view function's name or the name parameter's value, you just specify the string and it will match whichever one is available? If you're happy with that, the attached patch does the change (just to prove it's possible). I still need to update the docs, but I wanted to check on the interface first. It adds the url(...) function to urlpatterns and allows a "name" parameter (or optional fourth argument) in the pattern. That name is used in the match. The only place for confusion is if you have a name parameter that precisely matches an importable function. However, the alternative (to ensure that never happens) would mean you would need to somehow say "I am giving you a view function" or "I am giving you a name". FWIW, I don't think just doing the right thing is a violation of explicit vs. implicit, since names can be much more descriptive and views will generally contain dots, so the confusion is limited to people with unusual ideas about naming things. I don't like the suggestion elsewhere in this thread that we use string quoting in the template tag to suggest the name is being used. We already have an issue there that every other tag uses string quoting to suggest a literal and unquoted values to say "resolve the template variable to get the value". To wit: {% include "foo" %} vs. {% include foo %} (the latter resolves variable foo to get the value) whereas {% url foo %} uses the literal string foo. At some future time, I might be arguing we fix that prior to 1.0 because it's inconsistent and removes the ability to use a variable in the url tag. Cheers, Malcolm --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Django developers" group. To post to this group, send email to django-developers@googlegroups.com To unsubscribe from this group, send email to [EMAIL PROTECTED] For more options, visit this group at http://groups.google.com/group/django-developers?hl=en -~----------~----~----~----~------~----~------~--~---
Index: django/conf/urls/defaults.py =================================================================== --- django/conf/urls/defaults.py (revision 4836) +++ django/conf/urls/defaults.py (working copy) @@ -1,19 +1,25 @@ from django.core.urlresolvers import RegexURLPattern, RegexURLResolver -__all__ = ['handler404', 'handler500', 'include', 'patterns'] +__all__ = ['handler404', 'handler500', 'include', 'patterns', 'url'] handler404 = 'django.views.defaults.page_not_found' handler500 = 'django.views.defaults.server_error' include = lambda urlconf_module: [urlconf_module] -def patterns(prefix, *tuples): +def patterns(prefix, *args): pattern_list = [] - for t in tuples: - regex, view_or_include = t[:2] - default_kwargs = t[2:] - if type(view_or_include) == list: - pattern_list.append(RegexURLResolver(regex, view_or_include[0], *default_kwargs)) + for t in args: + if isinstance(t, (list, tuple)): + pattern_list.append(url(prefix=prefix, *t)) else: - pattern_list.append(RegexURLPattern(regex, prefix and (prefix + '.' + view_or_include) or view_or_include, *default_kwargs)) + pattern_list.append(t) return pattern_list + +def url(regex, view, kwargs=None, name=None, prefix=''): + if type(view) == list: + # For include(...) processing. + return RegexURLResolver(regex, view[0], kwargs) + else: + return RegexURLPattern(regex, prefix and (prefix + '.' + view) or view, kwargs, name) + Index: django/core/urlresolvers.py =================================================================== --- django/core/urlresolvers.py (revision 4836) +++ django/core/urlresolvers.py (working copy) @@ -88,7 +88,7 @@ return str(value) # TODO: Unicode? class RegexURLPattern(object): - def __init__(self, regex, callback, default_args=None): + def __init__(self, regex, callback, default_args=None, name=None): # regex is a string representing a regular expression. # callback is either a string like 'foo.views.news.stories.story_detail' # which represents the path to a module and a view function name, or a @@ -100,6 +100,7 @@ self._callback = None self._callback_str = callback self.default_args = default_args or {} + self.name = name def resolve(self, path): match = self.regex.search(path) @@ -205,14 +206,15 @@ try: lookup_view = getattr(__import__(mod_name, {}, {}, ['']), func_name) except (ImportError, AttributeError): - raise NoReverseMatch + if func_name != '': + raise NoReverseMatch for pattern in self.urlconf_module.urlpatterns: if isinstance(pattern, RegexURLResolver): try: return pattern.reverse_helper(lookup_view, *args, **kwargs) except NoReverseMatch: continue - elif pattern.callback == lookup_view: + elif pattern.callback == lookup_view or pattern.name == lookup_view: try: return pattern.reverse_helper(*args, **kwargs) except NoReverseMatch: Index: tests/regressiontests/templates/tests.py =================================================================== --- tests/regressiontests/templates/tests.py (revision 4836) +++ tests/regressiontests/templates/tests.py (working copy) @@ -692,11 +692,12 @@ 'url01' : ('{% url regressiontests.templates.views.client client.id %}', {'client': {'id': 1}}, '/url_tag/client/1/'), 'url02' : ('{% url regressiontests.templates.views.client_action client.id,action="update" %}', {'client': {'id': 1}}, '/url_tag/client/1/update/'), 'url03' : ('{% url regressiontests.templates.views.index %}', {}, '/url_tag/'), + 'url04' : ('{% url named-client client.id %}', {'client': {'id': 1}}, '/url_tag/named-client/1/'), # Failures - 'url04' : ('{% url %}', {}, template.TemplateSyntaxError), - 'url05' : ('{% url no_such_view %}', {}, ''), - 'url06' : ('{% url regressiontests.templates.views.client no_such_param="value" %}', {}, ''), + 'url-fail01' : ('{% url %}', {}, template.TemplateSyntaxError), + 'url-fail02' : ('{% url no_such_view %}', {}, ''), + 'url-fail03' : ('{% url regressiontests.templates.views.client no_such_param="value" %}', {}, ''), } # Register our custom template loader. Index: tests/regressiontests/templates/urls.py =================================================================== --- tests/regressiontests/templates/urls.py (revision 4836) +++ tests/regressiontests/templates/urls.py (working copy) @@ -7,4 +7,5 @@ (r'^$', views.index), (r'^client/(\d+)/$', views.client), (r'^client/(\d+)/(?P<action>[^/]+)/$', views.client_action), + url(r'^named-client/(\d+)/$', views.client, name="named-client"), )