mirror of
				https://github.com/django/django.git
				synced 2025-10-31 09:41:08 +00:00 
			
		
		
		
	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:
		| @@ -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) | ||||
|  | ||||
|   | ||||
| @@ -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,6 +206,7 @@ class RegexURLResolver(object): | ||||
|             try: | ||||
|                 lookup_view = getattr(__import__(mod_name, {}, {}, ['']), func_name) | ||||
|             except (ImportError, AttributeError): | ||||
|                 if func_name != '': | ||||
|                     raise NoReverseMatch | ||||
|         for pattern in self.urlconf_module.urlpatterns: | ||||
|             if isinstance(pattern, RegexURLResolver): | ||||
| @@ -212,7 +214,7 @@ class RegexURLResolver(object): | ||||
|                     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"), | ||||
| ) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user