Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Added the ability to name URL patterns. Helps with disambiguity rever…

…se matches.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@4901 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit d882656ea34119b2c371cc4adbe0aac180651cde 1 parent a071609
@malcolmt malcolmt authored
View
22 django/conf/urls/defaults.py
@@ -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)
+
View
8 django/core/urlresolvers.py
@@ -88,7 +88,7 @@ def __call__(self, match_obj):
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 @@ def __init__(self, regex, callback, default_args=None):
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 @@ def reverse(self, lookup_view, *args, **kwargs):
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:
View
62 docs/url_dispatch.txt
@@ -185,10 +185,25 @@ The first argument to ``patterns()`` is a string ``prefix``. See
The remaining arguments should be tuples in this format::
- (regular expression, Python callback function [, optional dictionary])
+ (regular expression, Python callback function [, optional dictionary [, optional name]])
-...where ``optional dictionary`` is optional. (See
-_`Passing extra options to view functions` below.)
+...where ``optional dictionary`` and ``optional name`` are optional. (See
+`Passing extra options to view functions`_ below.)
+
+url
+---
+**New in development version**
+
+The ``url()`` function can be used instead of a tuple as an argument to
+``patterns()``. This is convenient if you wish to specify a name without the
+optional extra arguments dictionary. For example::
+
+ urlpatterns = patterns('',
+ url(r'/index/$', index_view, name="main-view"),
+ ...
+ )
+
+See `Naming URL patterns`_ for why then ``name`` parameter is useful.
handler404
----------
@@ -479,3 +494,44 @@ The style you use is up to you.
Note that if you use this technique -- passing objects rather than strings --
the view prefix (as explained in "The view prefix" above) will have no effect.
+
+Naming URL patterns
+===================
+
+**New in development version**
+
+It is fairly common to use the same view function in multiple URL patterns in
+your URLConf. This leads to problems when you come to do reverse URL matching,
+because the ``permalink()`` decorator and ``{% url %}`` template tag use the
+name of the view function to find a match.
+
+To solve this problem, you can give a name to each of your URL patterns in
+order to distinguish them from other patterns using the same views and
+parameters. You can then use this name wherever you would otherwise use the
+name of the view function. For example, if you URLConf contains::
+
+ urlpatterns = patterns('',
+ url(r'/archive/(\d{4})/$', archive, name="full-archive"),
+ url(r'/archive-summary/(\d{4})/$', archive, {'summary': True}, "arch-summary"),
+ )
+
+...you could refer to either the summary archive view in a template as::
+
+ {% url arch-summary 1945 %}
+
+Even though both URL patterns refer to the ``archive`` view here, using the
+``name`` parameter to ``url()`` allows you to tell them apart in templates.
+
+The string used for the URL name can contain any characters you like. You are
+not restricted to valid Python names.
+
+.. note::
+
+ Make sure that when you name your URLs, you use names that are unlikely to
+ clash with any other application's choice of names. If you call your URL
+ pattern *comment* and another application does the same thing, there is no
+ guarantee which URL will be inserted into your template when you use this
+ name. Putting a prefix on your URL names, perhaps derived from
+ the application name, will decrease the chances of collision. Something
+ like *myapp-comment* is recommended over simply *comment*.
+
View
7 tests/regressiontests/templates/tests.py
@@ -692,11 +692,12 @@ def test_templates(self):
'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.
View
1  tests/regressiontests/templates/urls.py
@@ -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"),
)
Please sign in to comment.
Something went wrong with that request. Please try again.