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):
|
class RegexURLPattern(object):
|
||||||
def __init__(self, regex, callback, default_args=None):
|
def __init__(self, regex, callback, default_args=None):
|
||||||
# regex is a string representing a regular expression.
|
# regex is a string representing a regular expression.
|
||||||
# callback is something 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.
|
# which represents the path to a module and a view function name, or a
|
||||||
|
# callable object (view).
|
||||||
self.regex = re.compile(regex)
|
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 {}
|
self.default_args = default_args or {}
|
||||||
|
|
||||||
def resolve(self, path):
|
def resolve(self, path):
|
||||||
|
@ -106,23 +111,28 @@ class RegexURLPattern(object):
|
||||||
# In both cases, pass any extra_kwargs as **kwargs.
|
# In both cases, pass any extra_kwargs as **kwargs.
|
||||||
kwargs.update(self.default_args)
|
kwargs.update(self.default_args)
|
||||||
|
|
||||||
try: # Lazily load self.func.
|
return self.callback, args, kwargs
|
||||||
return self.func, args, kwargs
|
|
||||||
except AttributeError:
|
|
||||||
self.func = self.get_callback()
|
|
||||||
return self.func, args, kwargs
|
|
||||||
|
|
||||||
def get_callback(self):
|
def _get_callback(self):
|
||||||
mod_name, func_name = get_mod_func(self.callback)
|
if self._callback is not None:
|
||||||
|
return self._callback
|
||||||
|
mod_name, func_name = get_mod_func(self._callback_str)
|
||||||
try:
|
try:
|
||||||
return getattr(__import__(mod_name, '', '', ['']), func_name)
|
self._callback = getattr(__import__(mod_name, '', '', ['']), func_name)
|
||||||
except ImportError, e:
|
except ImportError, e:
|
||||||
raise ViewDoesNotExist, "Could not import %s. Error was: %s" % (mod_name, str(e))
|
raise ViewDoesNotExist, "Could not import %s. Error was: %s" % (mod_name, str(e))
|
||||||
except AttributeError, e:
|
except AttributeError, e:
|
||||||
raise ViewDoesNotExist, "Tried %s in module %s. Error was: %s" % (func_name, mod_name, str(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):
|
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
|
raise NoReverseMatch
|
||||||
return self.reverse_helper(*args, **kwargs)
|
return self.reverse_helper(*args, **kwargs)
|
||||||
|
|
||||||
|
@ -185,22 +195,28 @@ class RegexURLResolver(object):
|
||||||
def resolve500(self):
|
def resolve500(self):
|
||||||
return self._resolve_special('500')
|
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:
|
for pattern in self.urlconf_module.urlpatterns:
|
||||||
if isinstance(pattern, RegexURLResolver):
|
if isinstance(pattern, RegexURLResolver):
|
||||||
try:
|
try:
|
||||||
return pattern.reverse_helper(viewname, *args, **kwargs)
|
return pattern.reverse_helper(lookup_view, *args, **kwargs)
|
||||||
except NoReverseMatch:
|
except NoReverseMatch:
|
||||||
continue
|
continue
|
||||||
elif pattern.callback == viewname:
|
elif pattern.callback == lookup_view:
|
||||||
try:
|
try:
|
||||||
return pattern.reverse_helper(*args, **kwargs)
|
return pattern.reverse_helper(*args, **kwargs)
|
||||||
except NoReverseMatch:
|
except NoReverseMatch:
|
||||||
continue
|
continue
|
||||||
raise NoReverseMatch
|
raise NoReverseMatch
|
||||||
|
|
||||||
def reverse_helper(self, viewname, *args, **kwargs):
|
def reverse_helper(self, lookup_view, *args, **kwargs):
|
||||||
sub_match = self.reverse(viewname, *args, **kwargs)
|
sub_match = self.reverse(lookup_view, *args, **kwargs)
|
||||||
result = reverse_helper(self.regex, *args, **kwargs)
|
result = reverse_helper(self.regex, *args, **kwargs)
|
||||||
return result + sub_match
|
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
|
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
|
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.
|
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