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"),
 )

Reply via email to