mirror of
				https://github.com/django/django.git
				synced 2025-10-31 09:41:08 +00:00 
			
		
		
		
	Improved urlresolvers so that URLconfs can be passed objects instead of strings
git-svn-id: http://code.djangoproject.com/svn/django/trunk@3554 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
		| @@ -86,10 +86,15 @@ class MatchChecker(object): | ||||
| class RegexURLPattern(object): | ||||
|     def __init__(self, regex, callback, default_args=None): | ||||
|         # regex is a string representing a regular expression. | ||||
|         # callback is something like 'foo.views.news.stories.story_detail', | ||||
|         # which represents the path to a module and a view function name. | ||||
|         # 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 | ||||
|         # callable object (view). | ||||
|         self.regex = re.compile(regex) | ||||
|         self.callback = callback | ||||
|         if callable(callback): | ||||
|             self._callback = callback | ||||
|         else: | ||||
|             self._callback = None | ||||
|             self._callback_str = callback | ||||
|         self.default_args = default_args or {} | ||||
|  | ||||
|     def resolve(self, path): | ||||
| @@ -106,23 +111,28 @@ class RegexURLPattern(object): | ||||
|             # In both cases, pass any extra_kwargs as **kwargs. | ||||
|             kwargs.update(self.default_args) | ||||
|  | ||||
|             try: # Lazily load self.func. | ||||
|                 return self.func, args, kwargs | ||||
|             except AttributeError: | ||||
|                 self.func = self.get_callback() | ||||
|             return self.func, args, kwargs | ||||
|             return self.callback, args, kwargs | ||||
|  | ||||
|     def get_callback(self): | ||||
|         mod_name, func_name = get_mod_func(self.callback) | ||||
|     def _get_callback(self): | ||||
|         if self._callback is not None: | ||||
|             return self._callback | ||||
|         mod_name, func_name = get_mod_func(self._callback_str) | ||||
|         try: | ||||
|             return getattr(__import__(mod_name, '', '', ['']), func_name) | ||||
|             self._callback = getattr(__import__(mod_name, '', '', ['']), func_name) | ||||
|         except ImportError, e: | ||||
|             raise ViewDoesNotExist, "Could not import %s. Error was: %s" % (mod_name, str(e)) | ||||
|         except AttributeError, e: | ||||
|             raise ViewDoesNotExist, "Tried %s in module %s. Error was: %s" % (func_name, mod_name, str(e)) | ||||
|         return self._callback | ||||
|     callback = property(_get_callback) | ||||
|  | ||||
|     def reverse(self, viewname, *args, **kwargs): | ||||
|         if viewname != self.callback: | ||||
|         mod_name, func_name = get_mod_func(viewname) | ||||
|         try: | ||||
|             lookup_view = getattr(__import__(mod_name, '', '', ['']), func_name) | ||||
|         except (ImportError, AttributeError): | ||||
|             raise NoReverseMatch | ||||
|         if lookup_view != self.callback: | ||||
|             raise NoReverseMatch | ||||
|         return self.reverse_helper(*args, **kwargs) | ||||
|  | ||||
| @@ -185,22 +195,28 @@ class RegexURLResolver(object): | ||||
|     def resolve500(self): | ||||
|         return self._resolve_special('500') | ||||
|  | ||||
|     def reverse(self, viewname, *args, **kwargs): | ||||
|     def reverse(self, lookup_view, *args, **kwargs): | ||||
|         if not callable(lookup_view): | ||||
|             mod_name, func_name = get_mod_func(lookup_view) | ||||
|             try: | ||||
|                 lookup_view = getattr(__import__(mod_name, '', '', ['']), func_name) | ||||
|             except (ImportError, AttributeError): | ||||
|                 raise NoReverseMatch | ||||
|         for pattern in self.urlconf_module.urlpatterns: | ||||
|             if isinstance(pattern, RegexURLResolver): | ||||
|                 try: | ||||
|                     return pattern.reverse_helper(viewname, *args, **kwargs) | ||||
|                     return pattern.reverse_helper(lookup_view, *args, **kwargs) | ||||
|                 except NoReverseMatch: | ||||
|                     continue | ||||
|             elif pattern.callback == viewname: | ||||
|             elif pattern.callback == lookup_view: | ||||
|                 try: | ||||
|                     return pattern.reverse_helper(*args, **kwargs) | ||||
|                 except NoReverseMatch: | ||||
|                     continue | ||||
|         raise NoReverseMatch | ||||
|  | ||||
|     def reverse_helper(self, viewname, *args, **kwargs): | ||||
|         sub_match = self.reverse(viewname, *args, **kwargs) | ||||
|     def reverse_helper(self, lookup_view, *args, **kwargs): | ||||
|         sub_match = self.reverse(lookup_view, *args, **kwargs) | ||||
|         result = reverse_helper(self.regex, *args, **kwargs) | ||||
|         return result + sub_match | ||||
|  | ||||
|   | ||||
| @@ -431,3 +431,48 @@ Note that extra options will *always* be passed to *every* line in the included | ||||
| URLconf, regardless of whether the line's view actually accepts those options | ||||
| as valid. For this reason, this technique is only useful if you're certain that | ||||
| every view in the the included URLconf accepts the extra options you're passing. | ||||
|  | ||||
| Passing callable objects instead of strings | ||||
| =========================================== | ||||
|  | ||||
| **New in the Django development version.** | ||||
|  | ||||
| Some developers find it more natural to pass the actual Python function object | ||||
| rather than a string containing the path to its module. This alternative is | ||||
| supported -- you can pass any callable object as the view. | ||||
|  | ||||
| For example, given this URLconf in "string" notation:: | ||||
|  | ||||
|     urlpatterns = patterns('', | ||||
|         (r'^archive/$', 'mysite.views.archive'), | ||||
|         (r'^about/$', 'mysite.views.about'), | ||||
|         (r'^contact/$', 'mysite.views.contact'), | ||||
|     ) | ||||
|  | ||||
| You can accomplish the same thing by passing objects rather than strings. Just | ||||
| be sure to import the objects:: | ||||
|  | ||||
|     from mysite.views import archive, about, contact | ||||
|  | ||||
|     urlpatterns = patterns('', | ||||
|         (r'^archive/$', archive), | ||||
|         (r'^about/$', about), | ||||
|         (r'^contact/$', contact), | ||||
|     ) | ||||
|  | ||||
| The following example is functionally identical. It's just a bit more compact | ||||
| because it imports the module that contains the views, rather than importing | ||||
| each view individually:: | ||||
|  | ||||
|     from mysite import views | ||||
|  | ||||
|     urlpatterns = patterns('', | ||||
|         (r'^archive/$', views.archive), | ||||
|         (r'^about/$', views.about), | ||||
|         (r'^contact/$', views.contact), | ||||
|     ) | ||||
|  | ||||
| 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. | ||||
|   | ||||
		Reference in New Issue
	
	Block a user