|
@@ -1,640 +1,9 @@
|
|
|
-"""
|
|
|
-This module converts requested URLs to callback view functions.
|
|
|
+import warnings
|
|
|
|
|
|
-RegexURLResolver is the main class here. Its resolve() method takes a URL (as
|
|
|
-a string) and returns a ResolverMatch object which provides access to all
|
|
|
-attributes of the resolved URL match.
|
|
|
-"""
|
|
|
-from __future__ import unicode_literals
|
|
|
+from django.urls import * # NOQA
|
|
|
+from django.utils.deprecation import RemovedInDjango20Warning
|
|
|
|
|
|
-import functools
|
|
|
-import re
|
|
|
-from importlib import import_module
|
|
|
-from threading import local
|
|
|
-
|
|
|
-from django.core.exceptions import ImproperlyConfigured, ViewDoesNotExist
|
|
|
-from django.http import Http404
|
|
|
-from django.utils import lru_cache, six
|
|
|
-from django.utils.datastructures import MultiValueDict
|
|
|
-from django.utils.encoding import force_str, force_text, iri_to_uri
|
|
|
-from django.utils.functional import cached_property, lazy
|
|
|
-from django.utils.http import RFC3986_SUBDELIMS, urlquote
|
|
|
-from django.utils.module_loading import module_has_submodule
|
|
|
-from django.utils.regex_helper import normalize
|
|
|
-from django.utils.six.moves.urllib.parse import urlsplit, urlunsplit
|
|
|
-from django.utils.translation import get_language, override
|
|
|
-
|
|
|
-# SCRIPT_NAME prefixes for each thread are stored here. If there's no entry for
|
|
|
-# the current thread (which is the only one we ever access), it is assumed to
|
|
|
-# be empty.
|
|
|
-_prefixes = local()
|
|
|
-
|
|
|
-# Overridden URLconfs for each thread are stored here.
|
|
|
-_urlconfs = local()
|
|
|
-
|
|
|
-
|
|
|
-class ResolverMatch(object):
|
|
|
- def __init__(self, func, args, kwargs, url_name=None, app_names=None, namespaces=None):
|
|
|
- self.func = func
|
|
|
- self.args = args
|
|
|
- self.kwargs = kwargs
|
|
|
- self.url_name = url_name
|
|
|
-
|
|
|
- # If a URLRegexResolver doesn't have a namespace or app_name, it passes
|
|
|
- # in an empty value.
|
|
|
- self.app_names = [x for x in app_names if x] if app_names else []
|
|
|
- self.app_name = ':'.join(self.app_names)
|
|
|
-
|
|
|
- if namespaces:
|
|
|
- self.namespaces = [x for x in namespaces if x]
|
|
|
- else:
|
|
|
- self.namespaces = []
|
|
|
- self.namespace = ':'.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__])
|
|
|
-
|
|
|
- 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_names=%s, namespaces=%s)" % (
|
|
|
- self._func_path, self.args, self.kwargs, self.url_name, self.app_names, self.namespaces)
|
|
|
-
|
|
|
-
|
|
|
-class Resolver404(Http404):
|
|
|
- pass
|
|
|
-
|
|
|
-
|
|
|
-class NoReverseMatch(Exception):
|
|
|
- pass
|
|
|
-
|
|
|
-
|
|
|
-@lru_cache.lru_cache(maxsize=None)
|
|
|
-def get_callable(lookup_view):
|
|
|
- """
|
|
|
- Return a callable corresponding to lookup_view.
|
|
|
-
|
|
|
- * 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, otherwise raise an exception
|
|
|
- (ImportError or ViewDoesNotExist).
|
|
|
- """
|
|
|
- if callable(lookup_view):
|
|
|
- return lookup_view
|
|
|
-
|
|
|
- if not isinstance(lookup_view, six.string_types):
|
|
|
- raise ViewDoesNotExist("'%s' is not a callable or a dot-notation path" % lookup_view)
|
|
|
-
|
|
|
- mod_name, func_name = get_mod_func(lookup_view)
|
|
|
- if not func_name: # No '.' in lookup_view
|
|
|
- raise ImportError("Could not import '%s'. The path must be fully qualified." % lookup_view)
|
|
|
-
|
|
|
- try:
|
|
|
- mod = import_module(mod_name)
|
|
|
- except ImportError:
|
|
|
- parentmod, submod = get_mod_func(mod_name)
|
|
|
- if submod and not module_has_submodule(import_module(parentmod), submod):
|
|
|
- raise ViewDoesNotExist(
|
|
|
- "Could not import '%s'. Parent module %s does not exist." %
|
|
|
- (lookup_view, mod_name)
|
|
|
- )
|
|
|
- else:
|
|
|
- raise
|
|
|
- else:
|
|
|
- try:
|
|
|
- view_func = getattr(mod, func_name)
|
|
|
- except AttributeError:
|
|
|
- raise ViewDoesNotExist(
|
|
|
- "Could not import '%s'. View does not exist in module %s." %
|
|
|
- (lookup_view, mod_name)
|
|
|
- )
|
|
|
- else:
|
|
|
- if not callable(view_func):
|
|
|
- raise ViewDoesNotExist(
|
|
|
- "Could not import '%s.%s'. View is not callable." %
|
|
|
- (mod_name, func_name)
|
|
|
- )
|
|
|
- return view_func
|
|
|
-
|
|
|
-
|
|
|
-@lru_cache.lru_cache(maxsize=None)
|
|
|
-def get_resolver(urlconf=None):
|
|
|
- if urlconf is None:
|
|
|
- from django.conf import settings
|
|
|
- urlconf = settings.ROOT_URLCONF
|
|
|
- return RegexURLResolver(r'^/', urlconf)
|
|
|
-
|
|
|
-
|
|
|
-@lru_cache.lru_cache(maxsize=None)
|
|
|
-def get_ns_resolver(ns_pattern, resolver):
|
|
|
- # Build a namespaced resolver for the given parent URLconf pattern.
|
|
|
- # This makes it possible to have captured parameters in the parent
|
|
|
- # URLconf pattern.
|
|
|
- ns_resolver = RegexURLResolver(ns_pattern, resolver.url_patterns)
|
|
|
- return RegexURLResolver(r'^/', [ns_resolver])
|
|
|
-
|
|
|
-
|
|
|
-def get_mod_func(callback):
|
|
|
- # Converts 'django.views.news.stories.story_detail' to
|
|
|
- # ['django.views.news.stories', 'story_detail']
|
|
|
- try:
|
|
|
- dot = callback.rindex('.')
|
|
|
- except ValueError:
|
|
|
- return callback, ''
|
|
|
- return callback[:dot], callback[dot + 1:]
|
|
|
-
|
|
|
-
|
|
|
-class LocaleRegexProvider(object):
|
|
|
- """
|
|
|
- A mixin to provide a default regex property which can vary by active
|
|
|
- language.
|
|
|
- """
|
|
|
- def __init__(self, regex):
|
|
|
- # regex is either a string representing a regular expression, or a
|
|
|
- # translatable string (using ugettext_lazy) representing a regular
|
|
|
- # expression.
|
|
|
- self._regex = regex
|
|
|
- self._regex_dict = {}
|
|
|
-
|
|
|
- @property
|
|
|
- def regex(self):
|
|
|
- """
|
|
|
- Returns a compiled regular expression, depending upon the activated
|
|
|
- language-code.
|
|
|
- """
|
|
|
- language_code = get_language()
|
|
|
- if language_code not in self._regex_dict:
|
|
|
- if isinstance(self._regex, six.string_types):
|
|
|
- regex = self._regex
|
|
|
- else:
|
|
|
- regex = force_text(self._regex)
|
|
|
- try:
|
|
|
- compiled_regex = re.compile(regex, re.UNICODE)
|
|
|
- except re.error as e:
|
|
|
- raise ImproperlyConfigured(
|
|
|
- '"%s" is not a valid regular expression: %s' %
|
|
|
- (regex, six.text_type(e)))
|
|
|
-
|
|
|
- self._regex_dict[language_code] = compiled_regex
|
|
|
- return self._regex_dict[language_code]
|
|
|
-
|
|
|
-
|
|
|
-class RegexURLPattern(LocaleRegexProvider):
|
|
|
- def __init__(self, regex, callback, default_args=None, name=None):
|
|
|
- LocaleRegexProvider.__init__(self, regex)
|
|
|
- self.callback = callback # the view
|
|
|
- self.default_args = default_args or {}
|
|
|
- self.name = name
|
|
|
-
|
|
|
- def __repr__(self):
|
|
|
- return force_str('<%s %s %s>' % (self.__class__.__name__, self.name, self.regex.pattern))
|
|
|
-
|
|
|
- def resolve(self, path):
|
|
|
- match = self.regex.search(path)
|
|
|
- if match:
|
|
|
- # If there are any named groups, use those as kwargs, ignoring
|
|
|
- # non-named groups. Otherwise, pass all non-named arguments as
|
|
|
- # positional arguments.
|
|
|
- kwargs = match.groupdict()
|
|
|
- if kwargs:
|
|
|
- args = ()
|
|
|
- else:
|
|
|
- args = match.groups()
|
|
|
- # In both cases, pass any extra_kwargs as **kwargs.
|
|
|
- kwargs.update(self.default_args)
|
|
|
-
|
|
|
- return ResolverMatch(self.callback, args, kwargs, self.name)
|
|
|
-
|
|
|
- @cached_property
|
|
|
- def lookup_str(self):
|
|
|
- """
|
|
|
- A string that identifies the view (e.g. 'path.to.view_function' or
|
|
|
- 'path.to.ClassBasedView').
|
|
|
- """
|
|
|
- callback = self.callback
|
|
|
- if isinstance(callback, functools.partial):
|
|
|
- callback = callback.func
|
|
|
- if not hasattr(callback, '__name__'):
|
|
|
- return callback.__module__ + "." + callback.__class__.__name__
|
|
|
- else:
|
|
|
- return callback.__module__ + "." + callback.__name__
|
|
|
-
|
|
|
-
|
|
|
-class RegexURLResolver(LocaleRegexProvider):
|
|
|
- def __init__(self, regex, urlconf_name, default_kwargs=None, app_name=None, namespace=None):
|
|
|
- LocaleRegexProvider.__init__(self, regex)
|
|
|
- # urlconf_name is the dotted Python path to the module defining
|
|
|
- # urlpatterns. It may also be an object with an urlpatterns attribute
|
|
|
- # or urlpatterns itself.
|
|
|
- self.urlconf_name = urlconf_name
|
|
|
- self.callback = None
|
|
|
- self.default_kwargs = default_kwargs or {}
|
|
|
- self.namespace = namespace
|
|
|
- self.app_name = app_name
|
|
|
- self._reverse_dict = {}
|
|
|
- self._namespace_dict = {}
|
|
|
- self._app_dict = {}
|
|
|
- # set of dotted paths to all functions and classes that are used in
|
|
|
- # urlpatterns
|
|
|
- self._callback_strs = set()
|
|
|
- self._populated = False
|
|
|
-
|
|
|
- def __repr__(self):
|
|
|
- if isinstance(self.urlconf_name, list) and len(self.urlconf_name):
|
|
|
- # Don't bother to output the whole list, it can be huge
|
|
|
- urlconf_repr = '<%s list>' % self.urlconf_name[0].__class__.__name__
|
|
|
- else:
|
|
|
- urlconf_repr = repr(self.urlconf_name)
|
|
|
- return str('<%s %s (%s:%s) %s>') % (
|
|
|
- self.__class__.__name__, urlconf_repr, self.app_name,
|
|
|
- self.namespace, self.regex.pattern)
|
|
|
-
|
|
|
- def _populate(self):
|
|
|
- lookups = MultiValueDict()
|
|
|
- namespaces = {}
|
|
|
- apps = {}
|
|
|
- language_code = get_language()
|
|
|
- for pattern in reversed(self.url_patterns):
|
|
|
- if isinstance(pattern, RegexURLPattern):
|
|
|
- self._callback_strs.add(pattern.lookup_str)
|
|
|
- p_pattern = pattern.regex.pattern
|
|
|
- if p_pattern.startswith('^'):
|
|
|
- p_pattern = p_pattern[1:]
|
|
|
- if isinstance(pattern, RegexURLResolver):
|
|
|
- if pattern.namespace:
|
|
|
- namespaces[pattern.namespace] = (p_pattern, pattern)
|
|
|
- if pattern.app_name:
|
|
|
- apps.setdefault(pattern.app_name, []).append(pattern.namespace)
|
|
|
- else:
|
|
|
- parent_pat = pattern.regex.pattern
|
|
|
- for name in pattern.reverse_dict:
|
|
|
- for matches, pat, defaults in pattern.reverse_dict.getlist(name):
|
|
|
- new_matches = normalize(parent_pat + pat)
|
|
|
- lookups.appendlist(
|
|
|
- name,
|
|
|
- (
|
|
|
- new_matches,
|
|
|
- p_pattern + pat,
|
|
|
- dict(defaults, **pattern.default_kwargs),
|
|
|
- )
|
|
|
- )
|
|
|
- for namespace, (prefix, sub_pattern) in pattern.namespace_dict.items():
|
|
|
- namespaces[namespace] = (p_pattern + prefix, sub_pattern)
|
|
|
- for app_name, namespace_list in pattern.app_dict.items():
|
|
|
- apps.setdefault(app_name, []).extend(namespace_list)
|
|
|
- self._callback_strs.update(pattern._callback_strs)
|
|
|
- else:
|
|
|
- bits = normalize(p_pattern)
|
|
|
- lookups.appendlist(pattern.callback, (bits, p_pattern, pattern.default_args))
|
|
|
- if pattern.name is not None:
|
|
|
- lookups.appendlist(pattern.name, (bits, p_pattern, pattern.default_args))
|
|
|
- self._reverse_dict[language_code] = lookups
|
|
|
- self._namespace_dict[language_code] = namespaces
|
|
|
- self._app_dict[language_code] = apps
|
|
|
- self._populated = True
|
|
|
-
|
|
|
- @property
|
|
|
- def reverse_dict(self):
|
|
|
- language_code = get_language()
|
|
|
- if language_code not in self._reverse_dict:
|
|
|
- self._populate()
|
|
|
- return self._reverse_dict[language_code]
|
|
|
-
|
|
|
- @property
|
|
|
- def namespace_dict(self):
|
|
|
- language_code = get_language()
|
|
|
- if language_code not in self._namespace_dict:
|
|
|
- self._populate()
|
|
|
- return self._namespace_dict[language_code]
|
|
|
-
|
|
|
- @property
|
|
|
- def app_dict(self):
|
|
|
- language_code = get_language()
|
|
|
- if language_code not in self._app_dict:
|
|
|
- self._populate()
|
|
|
- return self._app_dict[language_code]
|
|
|
-
|
|
|
- def _is_callback(self, name):
|
|
|
- if not self._populated:
|
|
|
- self._populate()
|
|
|
- return name in self._callback_strs
|
|
|
-
|
|
|
- def resolve(self, path):
|
|
|
- path = force_text(path) # path may be a reverse_lazy object
|
|
|
- tried = []
|
|
|
- match = self.regex.search(path)
|
|
|
- if match:
|
|
|
- new_path = path[match.end():]
|
|
|
- for pattern in self.url_patterns:
|
|
|
- try:
|
|
|
- sub_match = pattern.resolve(new_path)
|
|
|
- except Resolver404 as e:
|
|
|
- sub_tried = e.args[0].get('tried')
|
|
|
- if sub_tried is not None:
|
|
|
- tried.extend([pattern] + t for t in sub_tried)
|
|
|
- else:
|
|
|
- tried.append([pattern])
|
|
|
- else:
|
|
|
- if sub_match:
|
|
|
- # Merge captured arguments in match with submatch
|
|
|
- sub_match_dict = dict(match.groupdict(), **self.default_kwargs)
|
|
|
- sub_match_dict.update(sub_match.kwargs)
|
|
|
-
|
|
|
- # If there are *any* named groups, ignore all non-named groups.
|
|
|
- # Otherwise, pass all non-named arguments as positional arguments.
|
|
|
- sub_match_args = sub_match.args
|
|
|
- if not sub_match_dict:
|
|
|
- sub_match_args = match.groups() + sub_match.args
|
|
|
-
|
|
|
- return ResolverMatch(
|
|
|
- sub_match.func,
|
|
|
- sub_match_args,
|
|
|
- sub_match_dict,
|
|
|
- sub_match.url_name,
|
|
|
- [self.app_name] + sub_match.app_names,
|
|
|
- [self.namespace] + sub_match.namespaces
|
|
|
- )
|
|
|
- tried.append([pattern])
|
|
|
- raise Resolver404({'tried': tried, 'path': new_path})
|
|
|
- raise Resolver404({'path': path})
|
|
|
-
|
|
|
- @cached_property
|
|
|
- def urlconf_module(self):
|
|
|
- if isinstance(self.urlconf_name, six.string_types):
|
|
|
- return import_module(self.urlconf_name)
|
|
|
- else:
|
|
|
- return self.urlconf_name
|
|
|
-
|
|
|
- @cached_property
|
|
|
- def url_patterns(self):
|
|
|
- # urlconf_module might be a valid set of patterns, so we default to it
|
|
|
- patterns = getattr(self.urlconf_module, "urlpatterns", self.urlconf_module)
|
|
|
- try:
|
|
|
- iter(patterns)
|
|
|
- except TypeError:
|
|
|
- msg = (
|
|
|
- "The included URLconf '{name}' does not appear to have any "
|
|
|
- "patterns in it. If you see valid patterns in the file then "
|
|
|
- "the issue is probably caused by a circular import."
|
|
|
- )
|
|
|
- raise ImproperlyConfigured(msg.format(name=self.urlconf_name))
|
|
|
- return patterns
|
|
|
-
|
|
|
- def resolve_error_handler(self, view_type):
|
|
|
- callback = getattr(self.urlconf_module, 'handler%s' % view_type, None)
|
|
|
- if not callback:
|
|
|
- # No handler specified in file; use default
|
|
|
- # Lazy import, since django.urls imports this file
|
|
|
- from django.conf import urls
|
|
|
- callback = getattr(urls, 'handler%s' % view_type)
|
|
|
- return get_callable(callback), {}
|
|
|
-
|
|
|
- def _reverse_with_prefix(self, lookup_view, _prefix, *args, **kwargs):
|
|
|
- if args and kwargs:
|
|
|
- raise ValueError("Don't mix *args and **kwargs in call to reverse()!")
|
|
|
- text_args = [force_text(v) for v in args]
|
|
|
- text_kwargs = {k: force_text(v) for (k, v) in kwargs.items()}
|
|
|
-
|
|
|
- if not self._populated:
|
|
|
- self._populate()
|
|
|
-
|
|
|
- possibilities = self.reverse_dict.getlist(lookup_view)
|
|
|
-
|
|
|
- for possibility, pattern, defaults in possibilities:
|
|
|
- for result, params in possibility:
|
|
|
- if args:
|
|
|
- if len(args) != len(params):
|
|
|
- continue
|
|
|
- candidate_subs = dict(zip(params, text_args))
|
|
|
- else:
|
|
|
- if (set(kwargs.keys()) | set(defaults.keys()) != set(params) |
|
|
|
- set(defaults.keys())):
|
|
|
- continue
|
|
|
- matches = True
|
|
|
- for k, v in defaults.items():
|
|
|
- if kwargs.get(k, v) != v:
|
|
|
- matches = False
|
|
|
- break
|
|
|
- if not matches:
|
|
|
- continue
|
|
|
- candidate_subs = text_kwargs
|
|
|
- # WSGI provides decoded URLs, without %xx escapes, and the URL
|
|
|
- # resolver operates on such URLs. First substitute arguments
|
|
|
- # without quoting to build a decoded URL and look for a match.
|
|
|
- # Then, if we have a match, redo the substitution with quoted
|
|
|
- # arguments in order to return a properly encoded URL.
|
|
|
- candidate_pat = _prefix.replace('%', '%%') + result
|
|
|
- if re.search('^%s%s' % (re.escape(_prefix), pattern), candidate_pat % candidate_subs, re.UNICODE):
|
|
|
- # safe characters from `pchar` definition of RFC 3986
|
|
|
- url = urlquote(candidate_pat % candidate_subs, safe=RFC3986_SUBDELIMS + str('/~:@'))
|
|
|
- # Don't allow construction of scheme relative urls.
|
|
|
- if url.startswith('//'):
|
|
|
- url = '/%%2F%s' % url[2:]
|
|
|
- return url
|
|
|
- # lookup_view can be URL name or callable, but callables are not
|
|
|
- # friendly in error messages.
|
|
|
- m = getattr(lookup_view, '__module__', None)
|
|
|
- n = getattr(lookup_view, '__name__', None)
|
|
|
- if m is not None and n is not None:
|
|
|
- lookup_view_s = "%s.%s" % (m, n)
|
|
|
- else:
|
|
|
- lookup_view_s = lookup_view
|
|
|
-
|
|
|
- patterns = [pattern for (possibility, pattern, defaults) in possibilities]
|
|
|
- raise NoReverseMatch("Reverse for '%s' with arguments '%s' and keyword "
|
|
|
- "arguments '%s' not found. %d pattern(s) tried: %s" %
|
|
|
- (lookup_view_s, args, kwargs, len(patterns), patterns))
|
|
|
-
|
|
|
-
|
|
|
-class LocaleRegexURLResolver(RegexURLResolver):
|
|
|
- """
|
|
|
- A URL resolver that always matches the active language code as URL prefix.
|
|
|
-
|
|
|
- Rather than taking a regex argument, we just override the ``regex``
|
|
|
- function to always return the active language-code as regex.
|
|
|
- """
|
|
|
- def __init__(self, urlconf_name, default_kwargs=None, app_name=None, namespace=None):
|
|
|
- super(LocaleRegexURLResolver, self).__init__(
|
|
|
- None, urlconf_name, default_kwargs, app_name, namespace)
|
|
|
-
|
|
|
- @property
|
|
|
- def regex(self):
|
|
|
- language_code = get_language()
|
|
|
- if language_code not in self._regex_dict:
|
|
|
- regex_compiled = re.compile('^%s/' % language_code, re.UNICODE)
|
|
|
- self._regex_dict[language_code] = regex_compiled
|
|
|
- return self._regex_dict[language_code]
|
|
|
-
|
|
|
-
|
|
|
-def resolve(path, urlconf=None):
|
|
|
- if urlconf is None:
|
|
|
- urlconf = get_urlconf()
|
|
|
- return get_resolver(urlconf).resolve(path)
|
|
|
-
|
|
|
-
|
|
|
-def reverse(viewname, urlconf=None, args=None, kwargs=None, current_app=None):
|
|
|
- if urlconf is None:
|
|
|
- urlconf = get_urlconf()
|
|
|
- resolver = get_resolver(urlconf)
|
|
|
- args = args or []
|
|
|
- kwargs = kwargs or {}
|
|
|
-
|
|
|
- prefix = get_script_prefix()
|
|
|
-
|
|
|
- if not isinstance(viewname, six.string_types):
|
|
|
- view = viewname
|
|
|
- else:
|
|
|
- parts = viewname.split(':')
|
|
|
- parts.reverse()
|
|
|
- view = parts[0]
|
|
|
- path = parts[1:]
|
|
|
-
|
|
|
- if current_app:
|
|
|
- current_path = current_app.split(':')
|
|
|
- current_path.reverse()
|
|
|
- else:
|
|
|
- current_path = None
|
|
|
-
|
|
|
- resolved_path = []
|
|
|
- ns_pattern = ''
|
|
|
- while path:
|
|
|
- ns = path.pop()
|
|
|
- current_ns = current_path.pop() if current_path else None
|
|
|
-
|
|
|
- # Lookup the name to see if it could be an app identifier
|
|
|
- try:
|
|
|
- app_list = resolver.app_dict[ns]
|
|
|
- # Yes! Path part matches an app in the current Resolver
|
|
|
- if current_ns and current_ns in app_list:
|
|
|
- # If we are reversing for a particular app,
|
|
|
- # use that namespace
|
|
|
- ns = current_ns
|
|
|
- elif ns not in app_list:
|
|
|
- # The name isn't shared by one of the instances
|
|
|
- # (i.e., the default) so just pick the first instance
|
|
|
- # as the default.
|
|
|
- ns = app_list[0]
|
|
|
- except KeyError:
|
|
|
- pass
|
|
|
-
|
|
|
- if ns != current_ns:
|
|
|
- current_path = None
|
|
|
-
|
|
|
- try:
|
|
|
- extra, resolver = resolver.namespace_dict[ns]
|
|
|
- resolved_path.append(ns)
|
|
|
- ns_pattern = ns_pattern + extra
|
|
|
- except KeyError as key:
|
|
|
- if resolved_path:
|
|
|
- raise NoReverseMatch(
|
|
|
- "%s is not a registered namespace inside '%s'" %
|
|
|
- (key, ':'.join(resolved_path)))
|
|
|
- else:
|
|
|
- raise NoReverseMatch("%s is not a registered namespace" %
|
|
|
- key)
|
|
|
- if ns_pattern:
|
|
|
- resolver = get_ns_resolver(ns_pattern, resolver)
|
|
|
-
|
|
|
- return force_text(iri_to_uri(resolver._reverse_with_prefix(view, prefix, *args, **kwargs)))
|
|
|
-
|
|
|
-reverse_lazy = lazy(reverse, six.text_type)
|
|
|
-
|
|
|
-
|
|
|
-def clear_url_caches():
|
|
|
- get_callable.cache_clear()
|
|
|
- get_resolver.cache_clear()
|
|
|
- get_ns_resolver.cache_clear()
|
|
|
-
|
|
|
-
|
|
|
-def set_script_prefix(prefix):
|
|
|
- """
|
|
|
- Sets the script prefix for the current thread.
|
|
|
- """
|
|
|
- if not prefix.endswith('/'):
|
|
|
- prefix += '/'
|
|
|
- _prefixes.value = prefix
|
|
|
-
|
|
|
-
|
|
|
-def get_script_prefix():
|
|
|
- """
|
|
|
- Returns the currently active script prefix. Useful for client code that
|
|
|
- wishes to construct their own URLs manually (although accessing the request
|
|
|
- instance is normally going to be a lot cleaner).
|
|
|
- """
|
|
|
- return getattr(_prefixes, "value", '/')
|
|
|
-
|
|
|
-
|
|
|
-def clear_script_prefix():
|
|
|
- """
|
|
|
- Unsets the script prefix for the current thread.
|
|
|
- """
|
|
|
- try:
|
|
|
- del _prefixes.value
|
|
|
- except AttributeError:
|
|
|
- pass
|
|
|
-
|
|
|
-
|
|
|
-def set_urlconf(urlconf_name):
|
|
|
- """
|
|
|
- Sets the URLconf for the current thread (overriding the default one in
|
|
|
- settings). Set to None to revert back to the default.
|
|
|
- """
|
|
|
- if urlconf_name:
|
|
|
- _urlconfs.value = urlconf_name
|
|
|
- else:
|
|
|
- if hasattr(_urlconfs, "value"):
|
|
|
- del _urlconfs.value
|
|
|
-
|
|
|
-
|
|
|
-def get_urlconf(default=None):
|
|
|
- """
|
|
|
- Returns the root URLconf to use for the current thread if it has been
|
|
|
- changed from the default one.
|
|
|
- """
|
|
|
- return getattr(_urlconfs, "value", default)
|
|
|
-
|
|
|
-
|
|
|
-def is_valid_path(path, urlconf=None):
|
|
|
- """
|
|
|
- Returns True if the given path resolves against the default URL resolver,
|
|
|
- False otherwise.
|
|
|
-
|
|
|
- This is a convenience method to make working with "is this a match?" cases
|
|
|
- easier, avoiding unnecessarily indented try...except blocks.
|
|
|
- """
|
|
|
- try:
|
|
|
- resolve(path, urlconf)
|
|
|
- return True
|
|
|
- except Resolver404:
|
|
|
- return False
|
|
|
-
|
|
|
-
|
|
|
-def translate_url(url, lang_code):
|
|
|
- """
|
|
|
- Given a URL (absolute or relative), try to get its translated version in
|
|
|
- the `lang_code` language (either by i18n_patterns or by translated regex).
|
|
|
- Return the original URL if no translated version is found.
|
|
|
- """
|
|
|
- parsed = urlsplit(url)
|
|
|
- try:
|
|
|
- match = resolve(parsed.path)
|
|
|
- except Resolver404:
|
|
|
- pass
|
|
|
- else:
|
|
|
- to_be_reversed = "%s:%s" % (match.namespace, match.url_name) if match.namespace else match.url_name
|
|
|
- with override(lang_code):
|
|
|
- try:
|
|
|
- url = reverse(to_be_reversed, args=match.args, kwargs=match.kwargs)
|
|
|
- except NoReverseMatch:
|
|
|
- pass
|
|
|
- else:
|
|
|
- url = urlunsplit((parsed.scheme, parsed.netloc, url, parsed.query, parsed.fragment))
|
|
|
- return url
|
|
|
+warnings.warn(
|
|
|
+ "Importing from django.core.urlresolvers is deprecated in favor of "
|
|
|
+ "django.urls.", RemovedInDjango20Warning, stacklevel=2
|
|
|
+)
|