Kaydet (Commit) d882656e authored tarafından Malcolm Tredinnick's avatar Malcolm Tredinnick

Added the ability to name URL patterns. Helps with disambiguity reverse matches.


git-svn-id: http://code.djangoproject.com/svn/django/trunk@4901 bcc190cf-cafb-0310-a4f2-bffc1f526a37
üst a071609a
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)
......@@ -88,7 +88,7 @@ class MatchChecker(object):
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 @@ class RegexURLPattern(object):
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 @@ class RegexURLResolver(object):
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:
......
......@@ -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*.
......@@ -692,11 +692,12 @@ class Templates(unittest.TestCase):
'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.
......
......@@ -7,4 +7,5 @@ urlpatterns = patterns('',
(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"),
)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment