mirror of
				https://github.com/django/django.git
				synced 2025-10-30 17:16:10 +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 | 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,6 +206,7 @@ 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): | ||||||
|  |                 if func_name != '': | ||||||
|                     raise NoReverseMatch |                     raise NoReverseMatch | ||||||
|         for pattern in self.urlconf_module.urlpatterns: |         for pattern in self.urlconf_module.urlpatterns: | ||||||
|             if isinstance(pattern, RegexURLResolver): |             if isinstance(pattern, RegexURLResolver): | ||||||
| @@ -212,7 +214,7 @@ class RegexURLResolver(object): | |||||||
|                     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"), | ||||||
| ) | ) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user