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:
parent
4805675f9d
commit
0b71ffacab
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue