änderung1
This commit is contained in:
1 parent
cc1d096bc3
commit
cbdda613b7
2098 files changed
+274524
No files matched your search
@@ -0,0 +1,23 @@
|
||||
from .base import (
|
||||
clear_script_prefix, clear_url_caches, get_script_prefix, get_urlconf,
|
||||
is_valid_path, resolve, reverse, reverse_lazy, set_script_prefix,
|
||||
set_urlconf, translate_url,
|
||||
)
|
||||
from .conf import include, path, re_path
|
||||
from .converters import register_converter
|
||||
from .exceptions import NoReverseMatch, Resolver404
|
||||
from .resolvers import (
|
||||
LocalePrefixPattern, ResolverMatch, URLPattern, URLResolver,
|
||||
get_ns_resolver, get_resolver,
|
||||
)
|
||||
from .utils import get_callable, get_mod_func
|
||||
|
||||
__all__ = [
|
||||
'LocalePrefixPattern', 'NoReverseMatch', 'URLPattern',
|
||||
'URLResolver', 'Resolver404', 'ResolverMatch', 'clear_script_prefix',
|
||||
'clear_url_caches', 'get_callable', 'get_mod_func', 'get_ns_resolver',
|
||||
'get_resolver', 'get_script_prefix', 'get_urlconf', 'include',
|
||||
'is_valid_path', 'path', 're_path', 'register_converter', 'resolve',
|
||||
'reverse', 'reverse_lazy', 'set_script_prefix', 'set_urlconf',
|
||||
'translate_url',
|
||||
]
|
||||
@@ -0,0 +1,183 @@
|
||||
from threading import local
|
||||
from urllib.parse import urlsplit, urlunsplit
|
||||
|
||||
from django.utils.encoding import iri_to_uri
|
||||
from django.utils.functional import lazy
|
||||
from django.utils.translation import override
|
||||
|
||||
from .exceptions import NoReverseMatch, Resolver404
|
||||
from .resolvers import get_ns_resolver, get_resolver
|
||||
from .utils import get_callable
|
||||
|
||||
# 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()
|
||||
|
||||
|
||||
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, str):
|
||||
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 = ''
|
||||
ns_converters = {}
|
||||
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 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
|
||||
ns_converters.update(resolver.pattern.converters)
|
||||
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, tuple(ns_converters.items()))
|
||||
|
||||
return iri_to_uri(resolver._reverse_with_prefix(view, prefix, *args, **kwargs))
|
||||
|
||||
|
||||
reverse_lazy = lazy(reverse, str)
|
||||
|
||||
|
||||
def clear_url_caches():
|
||||
get_callable.cache_clear()
|
||||
get_resolver.cache_clear()
|
||||
get_ns_resolver.cache_clear()
|
||||
|
||||
|
||||
def set_script_prefix(prefix):
|
||||
"""
|
||||
Set the script prefix for the current thread.
|
||||
"""
|
||||
if not prefix.endswith('/'):
|
||||
prefix += '/'
|
||||
_prefixes.value = prefix
|
||||
|
||||
|
||||
def get_script_prefix():
|
||||
"""
|
||||
Return 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():
|
||||
"""
|
||||
Unset the script prefix for the current thread.
|
||||
"""
|
||||
try:
|
||||
del _prefixes.value
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
|
||||
def set_urlconf(urlconf_name):
|
||||
"""
|
||||
Set the URLconf for the current thread (overriding the default one in
|
||||
settings). If urlconf_name is None, 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):
|
||||
"""
|
||||
Return 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):
|
||||
"""
|
||||
Return 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 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
|
||||
@@ -0,0 +1,77 @@
|
||||
"""Functions for use in URLsconfs."""
|
||||
from functools import partial
|
||||
from importlib import import_module
|
||||
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
|
||||
from .resolvers import (
|
||||
LocalePrefixPattern, RegexPattern, RoutePattern, URLPattern, URLResolver,
|
||||
)
|
||||
|
||||
|
||||
def include(arg, namespace=None):
|
||||
app_name = None
|
||||
if isinstance(arg, tuple):
|
||||
# Callable returning a namespace hint.
|
||||
try:
|
||||
urlconf_module, app_name = arg
|
||||
except ValueError:
|
||||
if namespace:
|
||||
raise ImproperlyConfigured(
|
||||
'Cannot override the namespace for a dynamic module that '
|
||||
'provides a namespace.'
|
||||
)
|
||||
raise ImproperlyConfigured(
|
||||
'Passing a %d-tuple to include() is not supported. Pass a '
|
||||
'2-tuple containing the list of patterns and app_name, and '
|
||||
'provide the namespace argument to include() instead.' % len(arg)
|
||||
)
|
||||
else:
|
||||
# No namespace hint - use manually provided namespace.
|
||||
urlconf_module = arg
|
||||
|
||||
if isinstance(urlconf_module, str):
|
||||
urlconf_module = import_module(urlconf_module)
|
||||
patterns = getattr(urlconf_module, 'urlpatterns', urlconf_module)
|
||||
app_name = getattr(urlconf_module, 'app_name', app_name)
|
||||
if namespace and not app_name:
|
||||
raise ImproperlyConfigured(
|
||||
'Specifying a namespace in include() without providing an app_name '
|
||||
'is not supported. Set the app_name attribute in the included '
|
||||
'module, or pass a 2-tuple containing the list of patterns and '
|
||||
'app_name instead.',
|
||||
)
|
||||
namespace = namespace or app_name
|
||||
# Make sure the patterns can be iterated through (without this, some
|
||||
# testcases will break).
|
||||
if isinstance(patterns, (list, tuple)):
|
||||
for url_pattern in patterns:
|
||||
pattern = getattr(url_pattern, 'pattern', None)
|
||||
if isinstance(pattern, LocalePrefixPattern):
|
||||
raise ImproperlyConfigured(
|
||||
'Using i18n_patterns in an included URLconf is not allowed.'
|
||||
)
|
||||
return (urlconf_module, app_name, namespace)
|
||||
|
||||
|
||||
def _path(route, view, kwargs=None, name=None, Pattern=None):
|
||||
if isinstance(view, (list, tuple)):
|
||||
# For include(...) processing.
|
||||
pattern = Pattern(route, is_endpoint=False)
|
||||
urlconf_module, app_name, namespace = view
|
||||
return URLResolver(
|
||||
pattern,
|
||||
urlconf_module,
|
||||
kwargs,
|
||||
app_name=app_name,
|
||||
namespace=namespace,
|
||||
)
|
||||
elif callable(view):
|
||||
pattern = Pattern(route, name=name, is_endpoint=True)
|
||||
return URLPattern(pattern, view, kwargs, name)
|
||||
else:
|
||||
raise TypeError('view must be a callable or a list/tuple in the case of include().')
|
||||
|
||||
|
||||
path = partial(_path, Pattern=RoutePattern)
|
||||
re_path = partial(_path, Pattern=RegexPattern)
|
||||
@@ -0,0 +1,66 @@
|
||||
import uuid
|
||||
from functools import lru_cache
|
||||
|
||||
|
||||
class IntConverter:
|
||||
regex = '[0-9]+'
|
||||
|
||||
def to_python(self, value):
|
||||
return int(value)
|
||||
|
||||
def to_url(self, value):
|
||||
return str(value)
|
||||
|
||||
|
||||
class StringConverter:
|
||||
regex = '[^/]+'
|
||||
|
||||
def to_python(self, value):
|
||||
return value
|
||||
|
||||
def to_url(self, value):
|
||||
return value
|
||||
|
||||
|
||||
class UUIDConverter:
|
||||
regex = '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}'
|
||||
|
||||
def to_python(self, value):
|
||||
return uuid.UUID(value)
|
||||
|
||||
def to_url(self, value):
|
||||
return str(value)
|
||||
|
||||
|
||||
class SlugConverter(StringConverter):
|
||||
regex = '[-a-zA-Z0-9_]+'
|
||||
|
||||
|
||||
class PathConverter(StringConverter):
|
||||
regex = '.+'
|
||||
|
||||
|
||||
DEFAULT_CONVERTERS = {
|
||||
'int': IntConverter(),
|
||||
'path': PathConverter(),
|
||||
'slug': SlugConverter(),
|
||||
'str': StringConverter(),
|
||||
'uuid': UUIDConverter(),
|
||||
}
|
||||
|
||||
|
||||
REGISTERED_CONVERTERS = {}
|
||||
|
||||
|
||||
def register_converter(converter, type_name):
|
||||
REGISTERED_CONVERTERS[type_name] = converter()
|
||||
get_converters.cache_clear()
|
||||
|
||||
|
||||
@lru_cache(maxsize=None)
|
||||
def get_converters():
|
||||
return {**DEFAULT_CONVERTERS, **REGISTERED_CONVERTERS}
|
||||
|
||||
|
||||
def get_converter(raw_converter):
|
||||
return get_converters()[raw_converter]
|
||||
@@ -0,0 +1,62 @@
|
||||
import functools
|
||||
from importlib import import_module
|
||||
|
||||
from django.core.exceptions import ViewDoesNotExist
|
||||
from django.utils.module_loading import module_has_submodule
|
||||
|
||||
|
||||
@functools.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, str):
|
||||
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
|
||||
|
||||
|
||||
def get_mod_func(callback):
|
||||
# Convert '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:]
|
||||
Reference in new issue
Block a user