mirror of https://github.com/django/django.git
Fixed #4566 -- Added caching speed-ups to reverse URL matching. Based on a
patch from smoo.master@gmail.com. git-svn-id: http://code.djangoproject.com/svn/django/trunk@5516 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
dfea6bdfa5
commit
5f5f1d913b
|
@ -9,8 +9,12 @@ a string) and returns a tuple in this format:
|
||||||
|
|
||||||
from django.http import Http404
|
from django.http import Http404
|
||||||
from django.core.exceptions import ImproperlyConfigured, ViewDoesNotExist
|
from django.core.exceptions import ImproperlyConfigured, ViewDoesNotExist
|
||||||
|
from django.utils.functional import memoize
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
_resolver_cache = {} # Maps urlconf modules to RegexURLResolver instances.
|
||||||
|
_callable_cache = {} # Maps view and url pattern names to their view functions.
|
||||||
|
|
||||||
class Resolver404(Http404):
|
class Resolver404(Http404):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -18,6 +22,21 @@ class NoReverseMatch(Exception):
|
||||||
# Don't make this raise an error when used in a template.
|
# Don't make this raise an error when used in a template.
|
||||||
silent_variable_failure = True
|
silent_variable_failure = True
|
||||||
|
|
||||||
|
def get_callable(lookup_view):
|
||||||
|
if not callable(lookup_view):
|
||||||
|
mod_name, func_name = get_mod_func(lookup_view)
|
||||||
|
if func_name != '':
|
||||||
|
lookup_view = getattr(__import__(mod_name, {}, {}, ['']), func_name)
|
||||||
|
return lookup_view
|
||||||
|
get_callable = memoize(get_callable, _callable_cache)
|
||||||
|
|
||||||
|
def get_resolver(urlconf):
|
||||||
|
if urlconf is None:
|
||||||
|
from django.conf import settings
|
||||||
|
urlconf = settings.ROOT_URLCONF
|
||||||
|
return RegexURLResolver(r'^/', urlconf)
|
||||||
|
get_resolver = memoize(get_resolver, _resolver_cache)
|
||||||
|
|
||||||
def get_mod_func(callback):
|
def get_mod_func(callback):
|
||||||
# Converts 'django.views.news.stories.story_detail' to
|
# Converts 'django.views.news.stories.story_detail' to
|
||||||
# ['django.views.news.stories', 'story_detail']
|
# ['django.views.news.stories', 'story_detail']
|
||||||
|
@ -129,9 +148,8 @@ class RegexURLPattern(object):
|
||||||
def _get_callback(self):
|
def _get_callback(self):
|
||||||
if self._callback is not None:
|
if self._callback is not None:
|
||||||
return self._callback
|
return self._callback
|
||||||
mod_name, func_name = get_mod_func(self._callback_str)
|
|
||||||
try:
|
try:
|
||||||
self._callback = getattr(__import__(mod_name, {}, {}, ['']), func_name)
|
self._callback = get_callable(self._callback_str)
|
||||||
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:
|
||||||
|
@ -160,6 +178,15 @@ class RegexURLResolver(object):
|
||||||
self.urlconf_name = urlconf_name
|
self.urlconf_name = urlconf_name
|
||||||
self.callback = None
|
self.callback = None
|
||||||
self.default_kwargs = default_kwargs or {}
|
self.default_kwargs = default_kwargs or {}
|
||||||
|
self.reverse_dict = {}
|
||||||
|
|
||||||
|
for pattern in reversed(self.urlconf_module.urlpatterns):
|
||||||
|
if isinstance(pattern, RegexURLResolver):
|
||||||
|
for key, value in pattern.reverse_dict.iteritems():
|
||||||
|
self.reverse_dict[key] = (pattern,) + value
|
||||||
|
else:
|
||||||
|
self.reverse_dict[pattern.callback] = (pattern,)
|
||||||
|
self.reverse_dict[pattern.name] = (pattern,)
|
||||||
|
|
||||||
def resolve(self, path):
|
def resolve(self, path):
|
||||||
tried = []
|
tried = []
|
||||||
|
@ -209,24 +236,12 @@ class RegexURLResolver(object):
|
||||||
return self._resolve_special('500')
|
return self._resolve_special('500')
|
||||||
|
|
||||||
def reverse(self, lookup_view, *args, **kwargs):
|
def reverse(self, lookup_view, *args, **kwargs):
|
||||||
if not callable(lookup_view):
|
try:
|
||||||
mod_name, func_name = get_mod_func(lookup_view)
|
lookup_view = get_callable(lookup_view)
|
||||||
try:
|
except (ImportError, AttributeError):
|
||||||
lookup_view = getattr(__import__(mod_name, {}, {}, ['']), func_name)
|
raise NoReverseMatch
|
||||||
except (ImportError, AttributeError):
|
if lookup_view in self.reverse_dict:
|
||||||
if func_name != '':
|
return ''.join([reverse_helper(part.regex, *args, **kwargs) for part in self.reverse_dict[lookup_view]])
|
||||||
raise NoReverseMatch
|
|
||||||
for pattern in self.urlconf_module.urlpatterns:
|
|
||||||
if isinstance(pattern, RegexURLResolver):
|
|
||||||
try:
|
|
||||||
return pattern.reverse_helper(lookup_view, *args, **kwargs)
|
|
||||||
except NoReverseMatch:
|
|
||||||
continue
|
|
||||||
elif pattern.callback == lookup_view or pattern.name == lookup_view:
|
|
||||||
try:
|
|
||||||
return pattern.reverse_helper(*args, **kwargs)
|
|
||||||
except NoReverseMatch:
|
|
||||||
continue
|
|
||||||
raise NoReverseMatch
|
raise NoReverseMatch
|
||||||
|
|
||||||
def reverse_helper(self, lookup_view, *args, **kwargs):
|
def reverse_helper(self, lookup_view, *args, **kwargs):
|
||||||
|
@ -235,17 +250,10 @@ class RegexURLResolver(object):
|
||||||
return result + sub_match
|
return result + sub_match
|
||||||
|
|
||||||
def resolve(path, urlconf=None):
|
def resolve(path, urlconf=None):
|
||||||
if urlconf is None:
|
return get_resolver(urlconf).resolve(path)
|
||||||
from django.conf import settings
|
|
||||||
urlconf = settings.ROOT_URLCONF
|
|
||||||
resolver = RegexURLResolver(r'^/', urlconf)
|
|
||||||
return resolver.resolve(path)
|
|
||||||
|
|
||||||
def reverse(viewname, urlconf=None, args=None, kwargs=None):
|
def reverse(viewname, urlconf=None, args=None, kwargs=None):
|
||||||
args = args or []
|
args = args or []
|
||||||
kwargs = kwargs or {}
|
kwargs = kwargs or {}
|
||||||
if urlconf is None:
|
return '/' + get_resolver(urlconf).reverse(viewname, *args, **kwargs)
|
||||||
from django.conf import settings
|
|
||||||
urlconf = settings.ROOT_URLCONF
|
|
||||||
resolver = RegexURLResolver(r'^/', urlconf)
|
|
||||||
return '/' + resolver.reverse(viewname, *args, **kwargs)
|
|
||||||
|
|
|
@ -3,6 +3,21 @@ def curry(_curried_func, *args, **kwargs):
|
||||||
return _curried_func(*(args+moreargs), **dict(kwargs, **morekwargs))
|
return _curried_func(*(args+moreargs), **dict(kwargs, **morekwargs))
|
||||||
return _curried
|
return _curried
|
||||||
|
|
||||||
|
def memoize(func, cache):
|
||||||
|
"""
|
||||||
|
Wrap a function so that results for any argument tuple are stored in
|
||||||
|
'cache'. Note that the args to the function must be usable as dictionary
|
||||||
|
keys.
|
||||||
|
"""
|
||||||
|
def wrapper(*args):
|
||||||
|
if args in cache:
|
||||||
|
return cache[args]
|
||||||
|
|
||||||
|
result = func(*args)
|
||||||
|
cache[args] = result
|
||||||
|
return result
|
||||||
|
return wrapper
|
||||||
|
|
||||||
class Promise:
|
class Promise:
|
||||||
"""
|
"""
|
||||||
This is just a base class for the proxy class created in
|
This is just a base class for the proxy class created in
|
||||||
|
|
Loading…
Reference in New Issue