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
This commit is contained in:
parent
a071609a70
commit
d882656ea3
|
@ -1,19 +1,25 @@
|
||||||
from django.core.urlresolvers import RegexURLPattern, RegexURLResolver
|
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'
|
handler404 = 'django.views.defaults.page_not_found'
|
||||||
handler500 = 'django.views.defaults.server_error'
|
handler500 = 'django.views.defaults.server_error'
|
||||||
|
|
||||||
include = lambda urlconf_module: [urlconf_module]
|
include = lambda urlconf_module: [urlconf_module]
|
||||||
|
|
||||||
def patterns(prefix, *tuples):
|
def patterns(prefix, *args):
|
||||||
pattern_list = []
|
pattern_list = []
|
||||||
for t in tuples:
|
for t in args:
|
||||||
regex, view_or_include = t[:2]
|
if isinstance(t, (list, tuple)):
|
||||||
default_kwargs = t[2:]
|
pattern_list.append(url(prefix=prefix, *t))
|
||||||
if type(view_or_include) == list:
|
|
||||||
pattern_list.append(RegexURLResolver(regex, view_or_include[0], *default_kwargs))
|
|
||||||
else:
|
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
|
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?
|
return str(value) # TODO: Unicode?
|
||||||
|
|
||||||
class RegexURLPattern(object):
|
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.
|
# regex is a string representing a regular expression.
|
||||||
# callback is either a string like 'foo.views.news.stories.story_detail'
|
# 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
|
# 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 = None
|
||||||
self._callback_str = callback
|
self._callback_str = callback
|
||||||
self.default_args = default_args or {}
|
self.default_args = default_args or {}
|
||||||
|
self.name = name
|
||||||
|
|
||||||
def resolve(self, path):
|
def resolve(self, path):
|
||||||
match = self.regex.search(path)
|
match = self.regex.search(path)
|
||||||
|
@ -205,14 +206,15 @@ class RegexURLResolver(object):
|
||||||
try:
|
try:
|
||||||
lookup_view = getattr(__import__(mod_name, {}, {}, ['']), func_name)
|
lookup_view = getattr(__import__(mod_name, {}, {}, ['']), func_name)
|
||||||
except (ImportError, AttributeError):
|
except (ImportError, AttributeError):
|
||||||
raise NoReverseMatch
|
if func_name != '':
|
||||||
|
raise NoReverseMatch
|
||||||
for pattern in self.urlconf_module.urlpatterns:
|
for pattern in self.urlconf_module.urlpatterns:
|
||||||
if isinstance(pattern, RegexURLResolver):
|
if isinstance(pattern, RegexURLResolver):
|
||||||
try:
|
try:
|
||||||
return pattern.reverse_helper(lookup_view, *args, **kwargs)
|
return pattern.reverse_helper(lookup_view, *args, **kwargs)
|
||||||
except NoReverseMatch:
|
except NoReverseMatch:
|
||||||
continue
|
continue
|
||||||
elif pattern.callback == lookup_view:
|
elif pattern.callback == lookup_view or pattern.name == lookup_view:
|
||||||
try:
|
try:
|
||||||
return pattern.reverse_helper(*args, **kwargs)
|
return pattern.reverse_helper(*args, **kwargs)
|
||||||
except NoReverseMatch:
|
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::
|
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
|
...where ``optional dictionary`` and ``optional name`` are optional. (See
|
||||||
_`Passing extra options to view functions` below.)
|
`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
|
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 --
|
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.
|
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/'),
|
'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/'),
|
'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/'),
|
'url03' : ('{% url regressiontests.templates.views.index %}', {}, '/url_tag/'),
|
||||||
|
'url04' : ('{% url named-client client.id %}', {'client': {'id': 1}}, '/url_tag/named-client/1/'),
|
||||||
|
|
||||||
# Failures
|
# Failures
|
||||||
'url04' : ('{% url %}', {}, template.TemplateSyntaxError),
|
'url-fail01' : ('{% url %}', {}, template.TemplateSyntaxError),
|
||||||
'url05' : ('{% url no_such_view %}', {}, ''),
|
'url-fail02' : ('{% url no_such_view %}', {}, ''),
|
||||||
'url06' : ('{% url regressiontests.templates.views.client no_such_param="value" %}', {}, ''),
|
'url-fail03' : ('{% url regressiontests.templates.views.client no_such_param="value" %}', {}, ''),
|
||||||
}
|
}
|
||||||
|
|
||||||
# Register our custom template loader.
|
# Register our custom template loader.
|
||||||
|
|
|
@ -7,4 +7,5 @@ urlpatterns = patterns('',
|
||||||
(r'^$', views.index),
|
(r'^$', views.index),
|
||||||
(r'^client/(\d+)/$', views.client),
|
(r'^client/(\d+)/$', views.client),
|
||||||
(r'^client/(\d+)/(?P<action>[^/]+)/$', views.client_action),
|
(r'^client/(\d+)/(?P<action>[^/]+)/$', views.client_action),
|
||||||
|
url(r'^named-client/(\d+)/$', views.client, name="named-client"),
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in New Issue