diff options
| author | Kevin Christopher Henry <k@severian.com> | 2013-10-03 03:20:04 -0400 |
|---|---|---|
| committer | Tim Graham <timograham@gmail.com> | 2014-04-23 20:12:34 -0400 |
| commit | 91afc00513bd2fa6302ea2be35e1f842cbd5fd38 (patch) | |
| tree | d46dd1b8dd88af40488d6e9290be8a45426f3bb3 /django | |
| parent | 0aa4c6c3911fd38567ea2d20c425d1e81e859ee1 (diff) | |
Fixed #21157 -- Fixed problems with ResolverMatch
- Fixed bug in get_callable() that caused resolve() to put a string
in ResolverMatch.func.
- Made ResolverMatch.url_name match the actual url name (or None).
- Updated tests that used the string value in ResolverMatch.func, and
added regression tests for this bug.
- Corrected test urls whose dummy view paths caused failures (behavior
that was previously masked by this bug).
Diffstat (limited to 'django')
| -rw-r--r-- | django/core/urlresolvers.py | 103 |
1 files changed, 59 insertions, 44 deletions
diff --git a/django/core/urlresolvers.py b/django/core/urlresolvers.py index 8ca602c241..2f05571687 100644 --- a/django/core/urlresolvers.py +++ b/django/core/urlresolvers.py @@ -39,34 +39,31 @@ class ResolverMatch(object): self.func = func self.args = args self.kwargs = kwargs + self.url_name = url_name self.app_name = app_name + if namespaces: self.namespaces = [x for x in namespaces if x] else: self.namespaces = [] - if not url_name: - if not hasattr(func, '__name__'): - # An instance of a callable class - url_name = '.'.join([func.__class__.__module__, func.__class__.__name__]) - else: - # A function - url_name = '.'.join([func.__module__, func.__name__]) - self.url_name = url_name + self.namespace = ':'.join(self.namespaces) - @property - def namespace(self): - return ':'.join(self.namespaces) + if not hasattr(func, '__name__'): + # A class-based view + self._func_path = '.'.join([func.__class__.__module__, func.__class__.__name__]) + else: + # A function-based view + self._func_path = '.'.join([func.__module__, func.__name__]) - @property - def view_name(self): - return ':'.join(filter(bool, (self.namespace, self.url_name))) + view_path = url_name or self._func_path + self.view_name = ':'.join(self.namespaces + [view_path]) def __getitem__(self, index): return (self.func, self.args, self.kwargs)[index] def __repr__(self): - return "ResolverMatch(func=%s, args=%s, kwargs=%s, url_name='%s', app_name='%s', namespace='%s')" % ( - self.func, self.args, self.kwargs, self.url_name, self.app_name, self.namespace) + return "ResolverMatch(func=%s, args=%s, kwargs=%s, url_name=%s, app_name=%s, namespaces=%s)" % ( + self._func_path, self.args, self.kwargs, self.url_name, self.app_name, self.namespaces) class Resolver404(Http404): @@ -80,43 +77,61 @@ class NoReverseMatch(Exception): @lru_cache.lru_cache(maxsize=None) def get_callable(lookup_view, can_fail=False): """ - Convert a string version of a function name to the callable object. - - If the lookup_view is not an import path, it is assumed to be a URL pattern - label and the original string is returned. + Return a callable corresponding to lookup_view. This function is used + by both resolve() and reverse(), so can_fail allows the caller to choose + between returning the input as is and raising an exception when the input + string can't be interpreted as an import path. - If can_fail is True, lookup_view might be a URL pattern label, so errors - during the import fail and the string is returned. + If lookup_view is already a callable, return it. + If lookup_view is a string import path that can be resolved to a callable, + import that callable and return it. + If lookup_view is some other kind of string and can_fail is True, the string + is returned as is. If can_fail is False, an exception is raised (either + ImportError or ViewDoesNotExist). """ - if not callable(lookup_view): - mod_name, func_name = get_mod_func(lookup_view) - if func_name == '': + if callable(lookup_view): + return lookup_view + + mod_name, func_name = get_mod_func(lookup_view) + if not func_name: # No '.' in lookup_view + if can_fail: return lookup_view + else: + raise ImportError( + "Could not import '%s'. The path must be fully qualified." % + lookup_view) - try: - mod = import_module(mod_name) - except ImportError: + try: + mod = import_module(mod_name) + except ImportError: + if can_fail: + return lookup_view + else: parentmod, submod = get_mod_func(mod_name) - if (not can_fail and submod != '' and - not module_has_submodule(import_module(parentmod), submod)): + if submod and not module_has_submodule(import_module(parentmod), submod): raise ViewDoesNotExist( - "Could not import %s. Parent module %s does not exist." % + "Could not import '%s'. Parent module %s does not exist." % (lookup_view, mod_name)) - if not can_fail: + else: raise + else: + try: + view_func = getattr(mod, func_name) + except AttributeError: + if can_fail: + return lookup_view + else: + raise ViewDoesNotExist( + "Could not import '%s'. View does not exist in module %s." % + (lookup_view, mod_name)) else: - try: - lookup_view = getattr(mod, func_name) - if not callable(lookup_view): - raise ViewDoesNotExist( - "Could not import %s.%s. View is not callable." % - (mod_name, func_name)) - except AttributeError: - if not can_fail: - raise ViewDoesNotExist( - "Could not import %s. View does not exist in module %s." % - (lookup_view, mod_name)) - return lookup_view + if not callable(view_func): + # For backwards compatibility this is raised regardless of can_fail + raise ViewDoesNotExist( + "Could not import '%s.%s'. View is not callable." % + (mod_name, func_name)) + + return view_func @lru_cache.lru_cache(maxsize=None) |
