From 16411b8400ad08f90c236bb2e18f65c655f903f8 Mon Sep 17 00:00:00 2001 From: Marten Kenbeek Date: Wed, 30 Dec 2015 16:51:16 +0100 Subject: [PATCH] Fixed #26013 -- Moved django.core.urlresolvers to django.urls. Thanks to Tim Graham for the review. --- django/__init__.py | 2 +- django/conf/urls/__init__.py | 2 +- django/conf/urls/i18n.py | 2 +- django/contrib/admin/models.py | 2 +- django/contrib/admin/options.py | 2 +- django/contrib/admin/sites.py | 2 +- .../contrib/admin/templatetags/admin_list.py | 2 +- .../contrib/admin/templatetags/admin_urls.py | 2 +- django/contrib/admin/utils.py | 2 +- django/contrib/admin/views/main.py | 2 +- django/contrib/admin/widgets.py | 2 +- django/contrib/admindocs/utils.py | 2 +- django/contrib/admindocs/views.py | 10 +- django/contrib/auth/admin.py | 2 +- django/contrib/auth/views.py | 2 +- django/contrib/flatpages/models.py | 2 +- django/contrib/gis/sitemaps/kml.py | 16 +- django/contrib/sitemaps/__init__.py | 13 +- django/contrib/sitemaps/views.py | 5 +- django/core/checks/urls.py | 4 +- django/core/handlers/base.py | 11 +- django/core/handlers/wsgi.py | 2 +- django/core/urlresolvers.py | 645 +----------------- django/db/models/__init__.py | 6 +- django/middleware/common.py | 6 +- django/middleware/csrf.py | 2 +- django/middleware/locale.py | 4 +- django/shortcuts.py | 14 +- django/template/defaulttags.py | 2 +- django/test/client.py | 5 +- django/test/signals.py | 2 +- django/test/utils.py | 2 +- django/urls/__init__.py | 20 + django/urls/base.py | 185 +++++ django/urls/exceptions.py | 11 + django/urls/resolvers.py | 393 +++++++++++ django/urls/utils.py | 64 ++ django/views/debug.py | 2 +- django/views/generic/base.py | 2 +- django/views/i18n.py | 2 +- .../writing-code/coding-style.txt | 2 +- docs/internals/deprecation.txt | 5 +- docs/intro/tutorial03.txt | 2 +- docs/intro/tutorial04.txt | 8 +- docs/intro/tutorial05.txt | 4 +- .../class-based-views/generic-date-based.txt | 2 +- .../ref/class-based-views/generic-editing.txt | 4 +- docs/ref/contrib/admin/index.txt | 9 +- docs/ref/contrib/sitemaps.txt | 7 +- docs/ref/contrib/syndication.txt | 2 +- docs/ref/exceptions.txt | 23 +- docs/ref/models/instances.txt | 8 +- docs/ref/request-response.txt | 12 +- docs/ref/templates/builtins.txt | 4 +- docs/ref/unicode.txt | 4 +- docs/ref/urlresolvers.txt | 50 +- docs/releases/1.1.txt | 10 +- docs/releases/1.10.txt | 7 +- docs/releases/1.4.11.txt | 14 +- docs/releases/1.4.12.txt | 5 +- docs/releases/1.4.14.txt | 4 +- docs/releases/1.4.txt | 4 +- docs/releases/1.5.6.txt | 14 +- docs/releases/1.5.7.txt | 4 +- docs/releases/1.5.9.txt | 4 +- docs/releases/1.5.txt | 4 +- docs/releases/1.6.3.txt | 14 +- docs/releases/1.6.4.txt | 8 +- docs/releases/1.6.6.txt | 4 +- docs/releases/1.6.txt | 25 +- docs/releases/1.7.3.txt | 3 +- docs/releases/1.8.txt | 9 +- docs/releases/1.9.txt | 10 +- .../class-based-views/generic-editing.txt | 8 +- docs/topics/class-based-views/mixins.txt | 6 +- docs/topics/http/shortcuts.txt | 7 +- docs/topics/http/urls.txt | 9 +- docs/topics/i18n/translation.txt | 9 +- docs/topics/templates.txt | 2 +- docs/topics/testing/tools.txt | 7 +- tests/admin_changelist/tests.py | 2 +- tests/admin_custom_urls/models.py | 2 +- tests/admin_custom_urls/tests.py | 2 +- tests/admin_docs/tests.py | 2 +- tests/admin_inlines/tests.py | 2 +- tests/admin_utils/test_logentry.py | 2 +- tests/admin_views/test_adminsite.py | 2 +- tests/admin_views/tests.py | 2 +- tests/admin_widgets/tests.py | 2 +- tests/auth_tests/test_views.py | 2 +- tests/forms_tests/tests/test_widgets.py | 2 +- tests/generic_inline_admin/tests.py | 2 +- tests/generic_views/models.py | 2 +- tests/generic_views/test_base.py | 2 +- tests/generic_views/test_edit.py | 2 +- tests/generic_views/views.py | 2 +- tests/i18n/patterns/tests.py | 2 +- tests/messages_tests/base.py | 2 +- tests/messages_tests/test_mixins.py | 2 +- tests/messages_tests/urls.py | 2 +- tests/proxy_models/tests.py | 2 +- tests/resolve_url/tests.py | 2 +- tests/sitemaps_tests/models.py | 2 +- tests/template_tests/syntax_tests/test_url.py | 2 +- tests/template_tests/tests.py | 8 +- tests/test_client/tests.py | 2 +- tests/test_client_regress/tests.py | 2 +- tests/test_utils/tests.py | 2 +- tests/timezones/tests.py | 2 +- tests/urlpatterns_reverse/middleware.py | 2 +- tests/urlpatterns_reverse/tests.py | 10 +- tests/urlpatterns_reverse/views.py | 2 +- tests/urlpatterns_reverse/views_broken.py | 2 +- .../management/commands/reverse_url.py | 2 +- tests/view_tests/tests/test_debug.py | 2 +- tests/view_tests/tests/test_i18n.py | 2 +- tests/view_tests/views.py | 2 +- 117 files changed, 961 insertions(+), 922 deletions(-) create mode 100644 django/urls/__init__.py create mode 100644 django/urls/base.py create mode 100644 django/urls/exceptions.py create mode 100644 django/urls/resolvers.py create mode 100644 django/urls/utils.py diff --git a/django/__init__.py b/django/__init__.py index c9c3ecf17ac..df8024a86e1 100644 --- a/django/__init__.py +++ b/django/__init__.py @@ -15,7 +15,7 @@ def setup(set_prefix=True): """ from django.apps import apps from django.conf import settings - from django.core.urlresolvers import set_script_prefix + from django.urls import set_script_prefix from django.utils.encoding import force_text from django.utils.log import configure_logging diff --git a/django/conf/urls/__init__.py b/django/conf/urls/__init__.py index 512d6f79e09..03879bccd4c 100644 --- a/django/conf/urls/__init__.py +++ b/django/conf/urls/__init__.py @@ -2,7 +2,7 @@ import warnings from importlib import import_module from django.core.exceptions import ImproperlyConfigured -from django.core.urlresolvers import ( +from django.urls import ( LocaleRegexURLResolver, RegexURLPattern, RegexURLResolver, ) from django.utils import six diff --git a/django/conf/urls/i18n.py b/django/conf/urls/i18n.py index 056b957ee0d..1ddf919132a 100644 --- a/django/conf/urls/i18n.py +++ b/django/conf/urls/i18n.py @@ -1,6 +1,6 @@ from django.conf import settings from django.conf.urls import url -from django.core.urlresolvers import LocaleRegexURLResolver +from django.urls import LocaleRegexURLResolver from django.views.i18n import set_language diff --git a/django/contrib/admin/models.py b/django/contrib/admin/models.py index d419ed2c41b..404fc6291f7 100644 --- a/django/contrib/admin/models.py +++ b/django/contrib/admin/models.py @@ -3,8 +3,8 @@ from __future__ import unicode_literals from django.conf import settings from django.contrib.admin.utils import quote from django.contrib.contenttypes.models import ContentType -from django.core.urlresolvers import NoReverseMatch, reverse from django.db import models +from django.urls import NoReverseMatch, reverse from django.utils import timezone from django.utils.encoding import python_2_unicode_compatible, smart_text from django.utils.translation import ugettext, ugettext_lazy as _ diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py index d30fe837568..fdc648060c4 100644 --- a/django/contrib/admin/options.py +++ b/django/contrib/admin/options.py @@ -24,7 +24,6 @@ from django.core.exceptions import ( FieldDoesNotExist, FieldError, PermissionDenied, ValidationError, ) from django.core.paginator import Paginator -from django.core.urlresolvers import reverse from django.db import models, router, transaction from django.db.models.constants import LOOKUP_SEP from django.db.models.fields import BLANK_CHOICE_DASH @@ -37,6 +36,7 @@ from django.forms.widgets import CheckboxSelectMultiple, SelectMultiple from django.http import Http404, HttpResponseRedirect from django.http.response import HttpResponseBase from django.template.response import SimpleTemplateResponse, TemplateResponse +from django.urls import reverse from django.utils import six from django.utils.decorators import method_decorator from django.utils.encoding import force_text, python_2_unicode_compatible diff --git a/django/contrib/admin/sites.py b/django/contrib/admin/sites.py index bba56655656..949a23c8aac 100644 --- a/django/contrib/admin/sites.py +++ b/django/contrib/admin/sites.py @@ -5,11 +5,11 @@ from django.conf import settings from django.contrib.admin import ModelAdmin, actions from django.contrib.auth import REDIRECT_FIELD_NAME from django.core.exceptions import ImproperlyConfigured, PermissionDenied -from django.core.urlresolvers import NoReverseMatch, reverse from django.db.models.base import ModelBase from django.http import Http404, HttpResponseRedirect from django.template.engine import Engine from django.template.response import TemplateResponse +from django.urls import NoReverseMatch, reverse from django.utils import six from django.utils.text import capfirst from django.utils.translation import ugettext as _, ugettext_lazy diff --git a/django/contrib/admin/templatetags/admin_list.py b/django/contrib/admin/templatetags/admin_list.py index 9cf279fa40f..a45cf74bf5f 100644 --- a/django/contrib/admin/templatetags/admin_list.py +++ b/django/contrib/admin/templatetags/admin_list.py @@ -11,11 +11,11 @@ from django.contrib.admin.views.main import ( ALL_VAR, ORDER_VAR, PAGE_VAR, SEARCH_VAR, ) from django.core.exceptions import ObjectDoesNotExist -from django.core.urlresolvers import NoReverseMatch from django.db import models from django.template import Library from django.template.loader import get_template from django.templatetags.static import static +from django.urls import NoReverseMatch from django.utils import formats from django.utils.deprecation import RemovedInDjango20Warning from django.utils.encoding import force_text diff --git a/django/contrib/admin/templatetags/admin_urls.py b/django/contrib/admin/templatetags/admin_urls.py index b19ee104a1d..8e665ec9de5 100644 --- a/django/contrib/admin/templatetags/admin_urls.py +++ b/django/contrib/admin/templatetags/admin_urls.py @@ -1,6 +1,6 @@ from django import template from django.contrib.admin.utils import quote -from django.core.urlresolvers import Resolver404, get_script_prefix, resolve +from django.urls import Resolver404, get_script_prefix, resolve from django.utils.http import urlencode from django.utils.six.moves.urllib.parse import parse_qsl, urlparse, urlunparse diff --git a/django/contrib/admin/utils.py b/django/contrib/admin/utils.py index e85e2de0d30..a6e61122bd7 100644 --- a/django/contrib/admin/utils.py +++ b/django/contrib/admin/utils.py @@ -6,12 +6,12 @@ from collections import defaultdict from django.contrib.auth import get_permission_codename from django.core.exceptions import FieldDoesNotExist -from django.core.urlresolvers import NoReverseMatch, reverse from django.db import models from django.db.models.constants import LOOKUP_SEP from django.db.models.deletion import Collector from django.db.models.sql.constants import QUERY_TERMS from django.forms.utils import pretty_name +from django.urls import NoReverseMatch, reverse from django.utils import formats, six, timezone from django.utils.encoding import force_str, force_text, smart_text from django.utils.html import format_html diff --git a/django/contrib/admin/views/main.py b/django/contrib/admin/views/main.py index b428473cf66..a0297cdfc5f 100644 --- a/django/contrib/admin/views/main.py +++ b/django/contrib/admin/views/main.py @@ -15,8 +15,8 @@ from django.core.exceptions import ( FieldDoesNotExist, ImproperlyConfigured, SuspiciousOperation, ) from django.core.paginator import InvalidPage -from django.core.urlresolvers import reverse from django.db import models +from django.urls import reverse from django.utils import six from django.utils.encoding import force_text from django.utils.http import urlencode diff --git a/django/contrib/admin/widgets.py b/django/contrib/admin/widgets.py index 3397e08a4b7..3424e7f0ab0 100644 --- a/django/contrib/admin/widgets.py +++ b/django/contrib/admin/widgets.py @@ -6,11 +6,11 @@ from __future__ import unicode_literals import copy from django import forms -from django.core.urlresolvers import reverse from django.db.models.deletion import CASCADE from django.forms.utils import flatatt from django.forms.widgets import RadioFieldRenderer from django.template.loader import render_to_string +from django.urls import reverse from django.utils import six from django.utils.encoding import force_text from django.utils.html import ( diff --git a/django/contrib/admindocs/utils.py b/django/contrib/admindocs/utils.py index 977fceced26..ecc7eafc72b 100644 --- a/django/contrib/admindocs/utils.py +++ b/django/contrib/admindocs/utils.py @@ -4,7 +4,7 @@ import re from email.errors import HeaderParseError from email.parser import HeaderParser -from django.core.urlresolvers import reverse +from django.urls import reverse from django.utils.encoding import force_bytes from django.utils.safestring import mark_safe diff --git a/django/contrib/admindocs/views.py b/django/contrib/admindocs/views.py index 996650cefb8..fb1afc296a5 100644 --- a/django/contrib/admindocs/views.py +++ b/django/contrib/admindocs/views.py @@ -8,11 +8,11 @@ from django.conf import settings from django.contrib import admin from django.contrib.admin.views.decorators import staff_member_required from django.contrib.admindocs import utils -from django.core import urlresolvers from django.core.exceptions import ImproperlyConfigured, ViewDoesNotExist from django.db import models from django.http import Http404 from django.template.engine import Engine +from django.urls import get_mod_func, get_resolver, get_urlconf, reverse from django.utils.decorators import method_decorator from django.utils.inspect import ( func_accepts_kwargs, func_accepts_var_args, func_has_no_args, @@ -38,7 +38,7 @@ class BaseAdminDocsView(TemplateView): return super(BaseAdminDocsView, self).dispatch(request, *args, **kwargs) def get_context_data(self, **kwargs): - kwargs.update({'root_path': urlresolvers.reverse('admin:index')}) + kwargs.update({'root_path': reverse('admin:index')}) kwargs.update(admin.site.each_context(self.request)) return super(BaseAdminDocsView, self).get_context_data(**kwargs) @@ -147,9 +147,9 @@ class ViewDetailView(BaseAdminDocsView): def get_context_data(self, **kwargs): view = self.kwargs['view'] - urlconf = urlresolvers.get_urlconf() - if urlresolvers.get_resolver(urlconf)._is_callback(view): - mod, func = urlresolvers.get_mod_func(view) + urlconf = get_urlconf() + if get_resolver(urlconf)._is_callback(view): + mod, func = get_mod_func(view) view_func = getattr(import_module(mod), func) else: raise Http404 diff --git a/django/contrib/auth/admin.py b/django/contrib/auth/admin.py index 37b68f222f2..e14ea24976d 100644 --- a/django/contrib/auth/admin.py +++ b/django/contrib/auth/admin.py @@ -9,10 +9,10 @@ from django.contrib.auth.forms import ( ) from django.contrib.auth.models import Group, User from django.core.exceptions import PermissionDenied -from django.core.urlresolvers import reverse from django.db import transaction from django.http import Http404, HttpResponseRedirect from django.template.response import TemplateResponse +from django.urls import reverse from django.utils.decorators import method_decorator from django.utils.encoding import force_text from django.utils.html import escape diff --git a/django/contrib/auth/views.py b/django/contrib/auth/views.py index 6362bf1daff..fee3067646e 100644 --- a/django/contrib/auth/views.py +++ b/django/contrib/auth/views.py @@ -13,10 +13,10 @@ from django.contrib.auth.forms import ( ) from django.contrib.auth.tokens import default_token_generator from django.contrib.sites.shortcuts import get_current_site -from django.core.urlresolvers import reverse from django.http import HttpResponseRedirect, QueryDict from django.shortcuts import resolve_url from django.template.response import TemplateResponse +from django.urls import reverse from django.utils.deprecation import RemovedInDjango20Warning from django.utils.encoding import force_text from django.utils.http import is_safe_url, urlsafe_base64_decode diff --git a/django/contrib/flatpages/models.py b/django/contrib/flatpages/models.py index 69e84f8670e..67e1a30bace 100644 --- a/django/contrib/flatpages/models.py +++ b/django/contrib/flatpages/models.py @@ -1,8 +1,8 @@ from __future__ import unicode_literals from django.contrib.sites.models import Site -from django.core.urlresolvers import get_script_prefix from django.db import models +from django.urls import get_script_prefix from django.utils.encoding import iri_to_uri, python_2_unicode_compatible from django.utils.translation import ugettext_lazy as _ diff --git a/django/contrib/gis/sitemaps/kml.py b/django/contrib/gis/sitemaps/kml.py index 83e1edcb619..a30d6fc9d5d 100644 --- a/django/contrib/gis/sitemaps/kml.py +++ b/django/contrib/gis/sitemaps/kml.py @@ -1,8 +1,8 @@ from django.apps import apps from django.contrib.gis.db.models.fields import GeometryField from django.contrib.sitemaps import Sitemap -from django.core import urlresolvers from django.db import models +from django.urls import reverse class KMLSitemap(Sitemap): @@ -56,12 +56,14 @@ class KMLSitemap(Sitemap): return self.locations def location(self, obj): - return urlresolvers.reverse('django.contrib.gis.sitemaps.views.%s' % self.geo_format, - kwargs={'label': obj[0], - 'model': obj[1], - 'field_name': obj[2], - } - ) + return reverse( + 'django.contrib.gis.sitemaps.views.%s' % self.geo_format, + kwargs={ + 'label': obj[0], + 'model': obj[1], + 'field_name': obj[2], + }, + ) class KMZSitemap(KMLSitemap): diff --git a/django/contrib/sitemaps/__init__.py b/django/contrib/sitemaps/__init__.py index 97f096a880a..dbbb1a00af3 100644 --- a/django/contrib/sitemaps/__init__.py +++ b/django/contrib/sitemaps/__init__.py @@ -1,7 +1,8 @@ from django.apps import apps as django_apps from django.conf import settings -from django.core import paginator, urlresolvers +from django.core import paginator from django.core.exceptions import ImproperlyConfigured +from django.urls import NoReverseMatch, reverse from django.utils import translation from django.utils.six.moves.urllib.parse import urlencode from django.utils.six.moves.urllib.request import urlopen @@ -18,17 +19,17 @@ def ping_google(sitemap_url=None, ping_url=PING_URL): Alerts Google that the sitemap for the current site has been updated. If sitemap_url is provided, it should be an absolute path to the sitemap for this site -- e.g., '/sitemap.xml'. If sitemap_url is not provided, this - function will attempt to deduce it by using urlresolvers.reverse(). + function will attempt to deduce it by using urls.reverse(). """ if sitemap_url is None: try: # First, try to get the "index" sitemap URL. - sitemap_url = urlresolvers.reverse('django.contrib.sitemaps.views.index') - except urlresolvers.NoReverseMatch: + sitemap_url = reverse('django.contrib.sitemaps.views.index') + except NoReverseMatch: try: # Next, try for the "global" sitemap URL. - sitemap_url = urlresolvers.reverse('django.contrib.sitemaps.views.sitemap') - except urlresolvers.NoReverseMatch: + sitemap_url = reverse('django.contrib.sitemaps.views.sitemap') + except NoReverseMatch: pass if sitemap_url is None: diff --git a/django/contrib/sitemaps/views.py b/django/contrib/sitemaps/views.py index e034533a8a8..660c283c536 100644 --- a/django/contrib/sitemaps/views.py +++ b/django/contrib/sitemaps/views.py @@ -3,10 +3,10 @@ from calendar import timegm from functools import wraps from django.contrib.sites.shortcuts import get_current_site -from django.core import urlresolvers from django.core.paginator import EmptyPage, PageNotAnInteger from django.http import Http404 from django.template.response import TemplateResponse +from django.urls import reverse from django.utils.http import http_date @@ -32,8 +32,7 @@ def index(request, sitemaps, if callable(site): site = site() protocol = req_protocol if site.protocol is None else site.protocol - sitemap_url = urlresolvers.reverse( - sitemap_url_name, kwargs={'section': section}) + sitemap_url = reverse(sitemap_url_name, kwargs={'section': section}) absolute_url = '%s://%s%s' % (protocol, req_site.domain, sitemap_url) sites.append(absolute_url) for page in range(2, site.paginator.num_pages + 1): diff --git a/django/core/checks/urls.py b/django/core/checks/urls.py index d2aac617394..a7fca3358fb 100644 --- a/django/core/checks/urls.py +++ b/django/core/checks/urls.py @@ -5,7 +5,7 @@ from . import Tags, Warning, register @register(Tags.urls) def check_url_config(app_configs, **kwargs): - from django.core.urlresolvers import get_resolver + from django.urls import get_resolver resolver = get_resolver() return check_resolver(resolver) @@ -14,7 +14,7 @@ def check_resolver(resolver): """ Recursively check the resolver. """ - from django.core.urlresolvers import RegexURLPattern, RegexURLResolver + from django.urls import RegexURLPattern, RegexURLResolver warnings = [] for pattern in resolver.url_patterns: if isinstance(pattern, RegexURLResolver): diff --git a/django/core/handlers/base.py b/django/core/handlers/base.py index 55aec8fc9bb..e45463cddbb 100644 --- a/django/core/handlers/base.py +++ b/django/core/handlers/base.py @@ -7,12 +7,13 @@ import warnings from django import http from django.conf import settings -from django.core import signals, urlresolvers +from django.core import signals from django.core.exceptions import ( MiddlewareNotUsed, PermissionDenied, SuspiciousOperation, ) from django.db import connections, transaction from django.http.multipartparser import MultiPartParserError +from django.urls import get_resolver, set_urlconf from django.utils import six from django.utils.deprecation import RemovedInDjango20Warning from django.utils.encoding import force_text @@ -111,8 +112,8 @@ class BaseHandler(object): # variable" exception in the event an exception is raised before # resolver is set urlconf = settings.ROOT_URLCONF - urlresolvers.set_urlconf(urlconf) - resolver = urlresolvers.get_resolver(urlconf) + set_urlconf(urlconf) + resolver = get_resolver(urlconf) # Use a flag to check if the response was rendered to prevent # multiple renderings or to force rendering if necessary. response_is_rendered = False @@ -128,8 +129,8 @@ class BaseHandler(object): if hasattr(request, 'urlconf'): # Reset url resolver with a custom URLconf. urlconf = request.urlconf - urlresolvers.set_urlconf(urlconf) - resolver = urlresolvers.get_resolver(urlconf) + set_urlconf(urlconf) + resolver = get_resolver(urlconf) resolver_match = resolver.resolve(request.path_info) callback, callback_args, callback_kwargs = resolver_match diff --git a/django/core/handlers/wsgi.py b/django/core/handlers/wsgi.py index 5ade1c3500a..29b4d02a1a2 100644 --- a/django/core/handlers/wsgi.py +++ b/django/core/handlers/wsgi.py @@ -12,7 +12,7 @@ from django import http from django.conf import settings from django.core import signals from django.core.handlers import base -from django.core.urlresolvers import set_script_prefix +from django.urls import set_script_prefix from django.utils import six from django.utils.encoding import force_str, force_text from django.utils.functional import cached_property diff --git a/django/core/urlresolvers.py b/django/core/urlresolvers.py index d37a40157df..c7b5d2570c9 100644 --- a/django/core/urlresolvers.py +++ b/django/core/urlresolvers.py @@ -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 +) diff --git a/django/db/models/__init__.py b/django/db/models/__init__.py index af497424fa9..5629f462772 100644 --- a/django/db/models/__init__.py +++ b/django/db/models/__init__.py @@ -26,15 +26,15 @@ from django.db.models.fields.related import ( # NOQA isort:skip def permalink(func): """ - Decorator that calls urlresolvers.reverse() to return a URL using - parameters returned by the decorated function "func". + Decorator that calls urls.reverse() to return a URL using parameters + returned by the decorated function "func". "func" should be a function that returns a tuple in one of the following formats: (viewname, viewargs) (viewname, viewargs, viewkwargs) """ - from django.core.urlresolvers import reverse + from django.urls import reverse @wraps(func) def inner(*args, **kwargs): diff --git a/django/middleware/common.py b/django/middleware/common.py index 3db925c8942..c7b1c1c772d 100644 --- a/django/middleware/common.py +++ b/django/middleware/common.py @@ -3,9 +3,9 @@ import re from django import http from django.conf import settings -from django.core import urlresolvers from django.core.exceptions import PermissionDenied from django.core.mail import mail_managers +from django.urls import is_valid_path from django.utils.cache import get_conditional_response, set_response_etag from django.utils.encoding import force_text from django.utils.six.moves.urllib.parse import urlparse @@ -74,8 +74,8 @@ class CommonMiddleware(object): if settings.APPEND_SLASH and not request.get_full_path().endswith('/'): urlconf = getattr(request, 'urlconf', None) return ( - not urlresolvers.is_valid_path(request.path_info, urlconf) - and urlresolvers.is_valid_path('%s/' % request.path_info, urlconf) + not is_valid_path(request.path_info, urlconf) + and is_valid_path('%s/' % request.path_info, urlconf) ) return False diff --git a/django/middleware/csrf.py b/django/middleware/csrf.py index 797b6f3eeed..835fee70a86 100644 --- a/django/middleware/csrf.py +++ b/django/middleware/csrf.py @@ -10,7 +10,7 @@ import logging import re from django.conf import settings -from django.core.urlresolvers import get_callable +from django.urls import get_callable from django.utils.cache import patch_vary_headers from django.utils.crypto import constant_time_compare, get_random_string from django.utils.encoding import force_text diff --git a/django/middleware/locale.py b/django/middleware/locale.py index 271a43cbfbd..063a4896bc5 100644 --- a/django/middleware/locale.py +++ b/django/middleware/locale.py @@ -1,10 +1,10 @@ "This is the locale selecting middleware that will look at accept headers" from django.conf import settings -from django.core.urlresolvers import ( +from django.http import HttpResponseRedirect +from django.urls import ( LocaleRegexURLResolver, get_resolver, get_script_prefix, is_valid_path, ) -from django.http import HttpResponseRedirect from django.utils import translation from django.utils.cache import patch_vary_headers from django.utils.functional import cached_property diff --git a/django/shortcuts.py b/django/shortcuts.py index b5f20b832c5..bd6362e8de4 100644 --- a/django/shortcuts.py +++ b/django/shortcuts.py @@ -3,7 +3,6 @@ This module collects helper functions and classes that "span" multiple levels of MVC. In other words, these functions/classes introduce controlled coupling for convenience's sake. """ -from django.core import urlresolvers from django.db.models.base import ModelBase from django.db.models.manager import Manager from django.db.models.query import QuerySet @@ -11,6 +10,7 @@ from django.http import ( Http404, HttpResponse, HttpResponsePermanentRedirect, HttpResponseRedirect, ) from django.template import loader +from django.urls import NoReverseMatch, reverse from django.utils import six from django.utils.encoding import force_text from django.utils.functional import Promise @@ -43,8 +43,8 @@ def redirect(to, *args, **kwargs): * A model: the model's `get_absolute_url()` function will be called. - * A view name, possibly with arguments: `urlresolvers.reverse()` will - be used to reverse-resolve the name. + * A view name, possibly with arguments: `urls.reverse()` will be used + to reverse-resolve the name. * A URL, which will be used as-is for the redirect location. @@ -123,8 +123,8 @@ def resolve_url(to, *args, **kwargs): * A model: the model's `get_absolute_url()` function will be called. - * A view name, possibly with arguments: `urlresolvers.reverse()` will - be used to reverse-resolve the name. + * A view name, possibly with arguments: `urls.reverse()` will be used + to reverse-resolve the name. * A URL, which will be returned as-is. """ @@ -144,8 +144,8 @@ def resolve_url(to, *args, **kwargs): # Next try a reverse URL resolution. try: - return urlresolvers.reverse(to, args=args, kwargs=kwargs) - except urlresolvers.NoReverseMatch: + return reverse(to, args=args, kwargs=kwargs) + except NoReverseMatch: # If this is a callable, re-raise. if callable(to): raise diff --git a/django/template/defaulttags.py b/django/template/defaulttags.py index 1f3cadee13c..5a972e3a797 100644 --- a/django/template/defaulttags.py +++ b/django/template/defaulttags.py @@ -426,7 +426,7 @@ class URLNode(Node): self.asvar = asvar def render(self, context): - from django.core.urlresolvers import reverse, NoReverseMatch + from django.urls import reverse, NoReverseMatch args = [arg.resolve(context) for arg in self.args] kwargs = { smart_text(k, 'ascii'): v.resolve(context) diff --git a/django/test/client.py b/django/test/client.py index 6730ac7acd8..a2eaf6da040 100644 --- a/django/test/client.py +++ b/django/test/client.py @@ -11,7 +11,6 @@ from io import BytesIO from django.apps import apps from django.conf import settings -from django.core import urlresolvers from django.core.handlers.base import BaseHandler from django.core.handlers.wsgi import ISO_8859_1, UTF_8, WSGIRequest from django.core.signals import ( @@ -22,6 +21,7 @@ from django.http import HttpRequest, QueryDict, SimpleCookie from django.template import TemplateDoesNotExist from django.test import signals from django.test.utils import ContextList +from django.urls import resolve from django.utils import six from django.utils.encoding import force_bytes, force_str, uri_to_iri from django.utils.functional import SimpleLazyObject, curry @@ -477,8 +477,7 @@ class Client(RequestFactory): response.json = curry(self._parse_json, response) # Attach the ResolverMatch instance to the response - response.resolver_match = SimpleLazyObject( - lambda: urlresolvers.resolve(request['PATH_INFO'])) + response.resolver_match = SimpleLazyObject(lambda: resolve(request['PATH_INFO'])) # Flatten a single context. Not really necessary anymore thanks to # the __getattr__ flattening in ContextList, but has some edge-case diff --git a/django/test/signals.py b/django/test/signals.py index 594004fd9d5..a647e2895be 100644 --- a/django/test/signals.py +++ b/django/test/signals.py @@ -145,7 +145,7 @@ def complex_setting_changed(**kwargs): @receiver(setting_changed) def root_urlconf_changed(**kwargs): if kwargs['setting'] == 'ROOT_URLCONF': - from django.core.urlresolvers import clear_url_caches, set_urlconf + from django.urls import clear_url_caches, set_urlconf clear_url_caches() set_urlconf(None) diff --git a/django/test/utils.py b/django/test/utils.py index e665ea1ec32..4e27af9004d 100644 --- a/django/test/utils.py +++ b/django/test/utils.py @@ -12,11 +12,11 @@ from django.apps import apps from django.conf import UserSettingsHolder, settings from django.core import mail from django.core.signals import request_started -from django.core.urlresolvers import get_script_prefix, set_script_prefix from django.db import reset_queries from django.http import request from django.template import Template from django.test.signals import setting_changed, template_rendered +from django.urls import get_script_prefix, set_script_prefix from django.utils import six from django.utils.decorators import ContextDecorator from django.utils.encoding import force_str diff --git a/django/urls/__init__.py b/django/urls/__init__.py new file mode 100644 index 00000000000..21b6da0a624 --- /dev/null +++ b/django/urls/__init__.py @@ -0,0 +1,20 @@ +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 .exceptions import NoReverseMatch, Resolver404 +from .resolvers import ( + LocaleRegexProvider, LocaleRegexURLResolver, RegexURLPattern, + RegexURLResolver, ResolverMatch, get_ns_resolver, get_resolver, +) +from .utils import get_callable, get_mod_func + +__all__ = [ + 'LocaleRegexProvider', 'LocaleRegexURLResolver', 'NoReverseMatch', + 'RegexURLPattern', 'RegexURLResolver', 'Resolver404', 'ResolverMatch', + 'clear_script_prefix', 'clear_url_caches', 'get_callable', 'get_mod_func', + 'get_ns_resolver', 'get_resolver', 'get_script_prefix', 'get_urlconf', + 'is_valid_path', 'resolve', 'reverse', 'reverse_lazy', 'set_script_prefix', + 'set_urlconf', 'translate_url', +] diff --git a/django/urls/base.py b/django/urls/base.py new file mode 100644 index 00000000000..a9a2dff1e6d --- /dev/null +++ b/django/urls/base.py @@ -0,0 +1,185 @@ +from __future__ import unicode_literals + +from threading import local + +from django.utils import six +from django.utils.encoding import force_text, iri_to_uri +from django.utils.functional import lazy +from django.utils.six.moves.urllib.parse import urlsplit, urlunsplit +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, 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 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): + """ + 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. + """ + from django.urls.base import resolve + 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. + """ + from django.urls import resolve, reverse + 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 diff --git a/django/urls/exceptions.py b/django/urls/exceptions.py new file mode 100644 index 00000000000..14967f7eb4a --- /dev/null +++ b/django/urls/exceptions.py @@ -0,0 +1,11 @@ +from __future__ import unicode_literals + +from django.http import Http404 + + +class Resolver404(Http404): + pass + + +class NoReverseMatch(Exception): + pass diff --git a/django/urls/resolvers.py b/django/urls/resolvers.py new file mode 100644 index 00000000000..7b9db5506fc --- /dev/null +++ b/django/urls/resolvers.py @@ -0,0 +1,393 @@ +""" +This module converts requested URLs to callback view functions. + +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 + +import functools +import re +from importlib import import_module + +from django.core.exceptions import ImproperlyConfigured +from django.utils import lru_cache, six +from django.utils.datastructures import MultiValueDict +from django.utils.encoding import force_str, force_text +from django.utils.functional import cached_property +from django.utils.http import RFC3986_SUBDELIMS, urlquote +from django.utils.regex_helper import normalize +from django.utils.translation import get_language + +from .exceptions import NoReverseMatch, Resolver404 +from .utils import get_callable + + +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) + self.namespaces = [x for x in namespaces if x] if namespaces else [] + 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, + ) + + +@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]) + + +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): + """ + Return a compiled regular expression based on the activate language. + """ + language_code = get_language() + if language_code not in self._regex_dict: + regex = self._regex if isinstance(self._regex, six.string_types) else 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() + args = () if kwargs else 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 lazy import, since + # django.conf.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] diff --git a/django/urls/utils.py b/django/urls/utils.py new file mode 100644 index 00000000000..1901c0a494b --- /dev/null +++ b/django/urls/utils.py @@ -0,0 +1,64 @@ +from __future__ import unicode_literals + +from importlib import import_module + +from django.core.exceptions import ViewDoesNotExist +from django.utils import lru_cache, six +from django.utils.module_loading import module_has_submodule + + +@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 + + +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:] diff --git a/django/views/debug.py b/django/views/debug.py index 977b1c65913..c29b71cce0d 100644 --- a/django/views/debug.py +++ b/django/views/debug.py @@ -5,10 +5,10 @@ import sys import types from django.conf import settings -from django.core.urlresolvers import Resolver404, resolve from django.http import HttpResponse, HttpResponseNotFound from django.template import Context, Engine, TemplateDoesNotExist from django.template.defaultfilters import force_escape, pprint +from django.urls import Resolver404, resolve from django.utils import lru_cache, six, timezone from django.utils.datastructures import MultiValueDict from django.utils.encoding import force_bytes, smart_text diff --git a/django/views/generic/base.py b/django/views/generic/base.py index 8f77fbfcb14..3352f343d04 100644 --- a/django/views/generic/base.py +++ b/django/views/generic/base.py @@ -5,8 +5,8 @@ from functools import update_wrapper from django import http from django.core.exceptions import ImproperlyConfigured -from django.core.urlresolvers import NoReverseMatch, reverse from django.template.response import TemplateResponse +from django.urls import NoReverseMatch, reverse from django.utils import six from django.utils.decorators import classonlymethod diff --git a/django/views/i18n.py b/django/views/i18n.py index 68039bcf2ad..37d38d9e740 100644 --- a/django/views/i18n.py +++ b/django/views/i18n.py @@ -6,8 +6,8 @@ import os from django import http from django.apps import apps from django.conf import settings -from django.core.urlresolvers import translate_url from django.template import Context, Engine +from django.urls import translate_url from django.utils import six from django.utils._os import upath from django.utils.encoding import smart_text diff --git a/docs/internals/contributing/writing-code/coding-style.txt b/docs/internals/contributing/writing-code/coding-style.txt index bdf1c4bb454..d560cfed19a 100644 --- a/docs/internals/contributing/writing-code/coding-style.txt +++ b/docs/internals/contributing/writing-code/coding-style.txt @@ -262,7 +262,7 @@ already been configured). So, if there is a module containing some code as follows:: from django.conf import settings - from django.core.urlresolvers import get_callable + from django.urls import get_callable default_foo_view = get_callable(settings.FOO_VIEW) diff --git a/docs/internals/deprecation.txt b/docs/internals/deprecation.txt index b165450d6a5..c03da8fd474 100644 --- a/docs/internals/deprecation.txt +++ b/docs/internals/deprecation.txt @@ -128,6 +128,8 @@ details on these changes. * The ``shell --plain`` option will be removed. +* The ``django.core.urlresolvers`` module will be removed. + .. _deprecation-removed-in-1.10: 1.10 @@ -152,8 +154,7 @@ details on these changes. * Using an incorrect count of unpacked values in the ``for`` template tag will raise an exception rather than fail silently. -* The ability to :func:`~django.core.urlresolvers.reverse` URLs using a dotted - Python path will be removed. +* The ability to reverse URLs using a dotted Python path will be removed. * Support for :py:mod:`optparse` will be dropped for custom management commands (replaced by :py:mod:`argparse`). diff --git a/docs/intro/tutorial03.txt b/docs/intro/tutorial03.txt index 2a6708f37b8..93cd2eb4d20 100644 --- a/docs/intro/tutorial03.txt +++ b/docs/intro/tutorial03.txt @@ -56,7 +56,7 @@ To get from a URL to a view, Django uses what are known as 'URLconfs'. A URLconf maps URL patterns (described as regular expressions) to views. This tutorial provides basic instruction in the use of URLconfs, and you can -refer to :mod:`django.core.urlresolvers` for more information. +refer to :mod:`django.urls` for more information. Writing more views ================== diff --git a/docs/intro/tutorial04.txt b/docs/intro/tutorial04.txt index 7c544b74367..201cc7d928b 100644 --- a/docs/intro/tutorial04.txt +++ b/docs/intro/tutorial04.txt @@ -71,7 +71,7 @@ create a real version. Add the following to ``polls/views.py``: from django.shortcuts import get_object_or_404, render from django.http import HttpResponseRedirect, HttpResponse - from django.core.urlresolvers import reverse + from django.urls import reverse from .models import Choice, Question # ... @@ -124,13 +124,13 @@ This code includes a few things we haven't covered yet in this tutorial: POST data. This tip isn't specific to Django; it's just good Web development practice. -* We are using the :func:`~django.core.urlresolvers.reverse` function in the +* We are using the :func:`~django.urls.reverse` function in the :class:`~django.http.HttpResponseRedirect` constructor in this example. This function helps avoid having to hardcode a URL in the view function. It is given the name of the view that we want to pass control to and the variable portion of the URL pattern that points to that view. In this case, using the URLconf we set up in :doc:`Tutorial 3 `, - this :func:`~django.core.urlresolvers.reverse` call will return a string like + this :func:`~django.urls.reverse` call will return a string like :: '/polls/3/results/' @@ -264,7 +264,7 @@ views and use Django's generic views instead. To do so, open the from django.shortcuts import get_object_or_404, render from django.http import HttpResponseRedirect - from django.core.urlresolvers import reverse + from django.urls import reverse from django.views import generic from .models import Choice, Question diff --git a/docs/intro/tutorial05.txt b/docs/intro/tutorial05.txt index 5cc4c2a9636..45fdb3746d8 100644 --- a/docs/intro/tutorial05.txt +++ b/docs/intro/tutorial05.txt @@ -362,7 +362,7 @@ With that ready, we can ask the client to do some work for us:: 404 >>> # on the other hand we should expect to find something at '/polls/' >>> # we'll use 'reverse()' rather than a hardcoded URL - >>> from django.core.urlresolvers import reverse + >>> from django.urls import reverse >>> response = client.get(reverse('polls:index')) >>> response.status_code 200 @@ -447,7 +447,7 @@ Add the following to ``polls/tests.py``: .. snippet:: :filename: polls/tests.py - from django.core.urlresolvers import reverse + from django.urls import reverse and we'll create a shortcut function to create questions as well as a new test class: diff --git a/docs/ref/class-based-views/generic-date-based.txt b/docs/ref/class-based-views/generic-date-based.txt index d8b79331383..b03e34858ea 100644 --- a/docs/ref/class-based-views/generic-date-based.txt +++ b/docs/ref/class-based-views/generic-date-based.txt @@ -13,7 +13,7 @@ views for displaying drilldown pages for date-based data. defined as follows in ``myapp/models.py``:: from django.db import models - from django.core.urlresolvers import reverse + from django.urls import reverse class Article(models.Model): title = models.CharField(max_length=200) diff --git a/docs/ref/class-based-views/generic-editing.txt b/docs/ref/class-based-views/generic-editing.txt index bb83fa597ee..d726c0a3cbd 100644 --- a/docs/ref/class-based-views/generic-editing.txt +++ b/docs/ref/class-based-views/generic-editing.txt @@ -15,7 +15,7 @@ editing content: Some of the examples on this page assume that an ``Author`` model has been defined as follows in ``myapp/models.py``:: - from django.core.urlresolvers import reverse + from django.urls import reverse from django.db import models class Author(models.Model): @@ -227,7 +227,7 @@ DeleteView **Example myapp/views.py**:: from django.views.generic.edit import DeleteView - from django.core.urlresolvers import reverse_lazy + from django.urls import reverse_lazy from myapp.models import Author class AuthorDelete(DeleteView): diff --git a/docs/ref/contrib/admin/index.txt b/docs/ref/contrib/admin/index.txt index a36859c0125..35c787ec1b9 100644 --- a/docs/ref/contrib/admin/index.txt +++ b/docs/ref/contrib/admin/index.txt @@ -1252,7 +1252,7 @@ subclass:: For example:: from django.contrib import admin - from django.core.urlresolvers import reverse + from django.urls import reverse class PersonAdmin(admin.ModelAdmin): def view_on_site(self, obj): @@ -2883,9 +2883,9 @@ So - if you wanted to get a reference to the Change view for a particular ``Choice`` object (from the polls application) in the default admin, you would call:: - >>> from django.core import urlresolvers + >>> from django.urls import reverse >>> c = Choice.objects.get(...) - >>> change_url = urlresolvers.reverse('admin:polls_choice_change', args=(c.id,)) + >>> change_url = reverse('admin:polls_choice_change', args=(c.id,)) This will find the first registered instance of the admin application (whatever the instance name), and resolve to the view for changing @@ -2896,8 +2896,7 @@ that instance as a ``current_app`` hint to the reverse call. For example, if you specifically wanted the admin view from the admin instance named ``custom``, you would need to call:: - >>> change_url = urlresolvers.reverse('admin:polls_choice_change', - ... args=(c.id,), current_app='custom') + >>> change_url = reverse('admin:polls_choice_change', args=(c.id,), current_app='custom') For more details, see the documentation on :ref:`reversing namespaced URLs `. diff --git a/docs/ref/contrib/sitemaps.txt b/docs/ref/contrib/sitemaps.txt index a4dbb3d1a14..822b338e2e0 100644 --- a/docs/ref/contrib/sitemaps.txt +++ b/docs/ref/contrib/sitemaps.txt @@ -300,13 +300,12 @@ Sitemap for static views Often you want the search engine crawlers to index views which are neither object detail pages nor flatpages. The solution is to explicitly list URL -names for these views in ``items`` and call -:func:`~django.core.urlresolvers.reverse` in the ``location`` method of -the sitemap. For example:: +names for these views in ``items`` and call :func:`~django.urls.reverse` in +the ``location`` method of the sitemap. For example:: # sitemaps.py from django.contrib import sitemaps - from django.core.urlresolvers import reverse + from django.urls import reverse class StaticViewSitemap(sitemaps.Sitemap): priority = 0.5 diff --git a/docs/ref/contrib/syndication.txt b/docs/ref/contrib/syndication.txt index 683b5cdc085..080b0a526c9 100644 --- a/docs/ref/contrib/syndication.txt +++ b/docs/ref/contrib/syndication.txt @@ -53,7 +53,7 @@ This simple example, taken from a hypothetical police beat news site describes a feed of the latest five news items:: from django.contrib.syndication.views import Feed - from django.core.urlresolvers import reverse + from django.urls import reverse from policebeat.models import NewsItem class LatestEntriesFeed(Feed): diff --git a/docs/ref/exceptions.txt b/docs/ref/exceptions.txt index 7e03c422862..37424fe7c68 100644 --- a/docs/ref/exceptions.txt +++ b/docs/ref/exceptions.txt @@ -84,7 +84,7 @@ Django core exception classes are defined in ``django.core.exceptions``. .. exception:: ViewDoesNotExist The :exc:`ViewDoesNotExist` exception is raised by - :mod:`django.core.urlresolvers` when a requested view does not exist. + :mod:`django.urls` when a requested view does not exist. ``MiddlewareNotUsed`` --------------------- @@ -142,12 +142,18 @@ or model are classified as ``NON_FIELD_ERRORS``. This constant is used as a key in dictionaries that otherwise map fields to their respective list of errors. -.. currentmodule:: django.core.urlresolvers +.. currentmodule:: django.urls URL Resolver exceptions ======================= -URL Resolver exceptions are defined in ``django.core.urlresolvers``. +URL Resolver exceptions are defined in ``django.urls``. + +.. deprecated:: 1.10 + + In older versions, these exceptions are located in + ``django.core.urlresolvers``. Importing from the old location will continue + to work until Django 2.0. ``Resolver404`` --------------- @@ -155,18 +161,17 @@ URL Resolver exceptions are defined in ``django.core.urlresolvers``. .. exception:: Resolver404 The :exc:`Resolver404` exception is raised by - :func:`django.core.urlresolvers.resolve()` if the path passed to - ``resolve()`` doesn't map to a view. It's a subclass of - :class:`django.http.Http404`. + :func:`~django.urls.resolve()` if the path passed to ``resolve()`` doesn't + map to a view. It's a subclass of :class:`django.http.Http404`. ``NoReverseMatch`` ------------------ .. exception:: NoReverseMatch - The :exc:`NoReverseMatch` exception is raised by - :mod:`django.core.urlresolvers` when a matching URL in your URLconf - cannot be identified based on the parameters supplied. + The :exc:`NoReverseMatch` exception is raised by :mod:`django.urls` when a + matching URL in your URLconf cannot be identified based on the parameters + supplied. .. currentmodule:: django.db diff --git a/docs/ref/models/instances.txt b/docs/ref/models/instances.txt index e8849d74050..f1fc3e60e66 100644 --- a/docs/ref/models/instances.txt +++ b/docs/ref/models/instances.txt @@ -672,14 +672,14 @@ For example:: def get_absolute_url(self): return "/people/%i/" % self.id -(Whilst this code is correct and simple, it may not be the most portable way to -write this kind of method. The :func:`~django.core.urlresolvers.reverse` -function is usually the best approach.) +While this code is correct and simple, it may not be the most portable way to +to write this kind of method. The :func:`~django.urls.reverse` function is +usually the best approach. For example:: def get_absolute_url(self): - from django.core.urlresolvers import reverse + from django.urls import reverse return reverse('people.views.details', args=[str(self.id)]) One place Django uses ``get_absolute_url()`` is in the admin app. If an object diff --git a/docs/ref/request-response.txt b/docs/ref/request-response.txt index 675a47d8d06..99a531adfbd 100644 --- a/docs/ref/request-response.txt +++ b/docs/ref/request-response.txt @@ -160,11 +160,11 @@ All attributes should be considered read-only, unless stated otherwise. .. attribute:: HttpRequest.resolver_match - An instance of :class:`~django.core.urlresolvers.ResolverMatch` representing - the resolved url. This attribute is only set after url resolving took place, - which means it's available in all views but not in middleware methods which - are executed before url resolving takes place (like ``process_request``, you - can use ``process_view`` instead). + An instance of :class:`~django.urls.ResolverMatch` representing the + resolved URL. This attribute is only set after URL resolving took place, + which means it's available in all views but not in middleware methods + which are executed before URL resolving takes place (like + ``process_request()``, you can use ``process_view()`` instead). Attributes set by application code ---------------------------------- @@ -175,7 +175,7 @@ application. .. attribute:: HttpRequest.current_app The :ttag:`url` template tag will use its value as the ``current_app`` - argument to :func:`~django.core.urlresolvers.reverse()`. + argument to :func:`~django.urls.reverse()`. .. attribute:: HttpRequest.urlconf diff --git a/docs/ref/templates/builtins.txt b/docs/ref/templates/builtins.txt index da6df4472fa..67e19769ad8 100644 --- a/docs/ref/templates/builtins.txt +++ b/docs/ref/templates/builtins.txt @@ -1024,8 +1024,8 @@ such as this: The template tag will output the string ``/clients/client/123/``. Note that if the URL you're reversing doesn't exist, you'll get an -:exc:`~django.core.urlresolvers.NoReverseMatch` exception raised, which will -cause your site to display an error page. +:exc:`~django.urls.NoReverseMatch` exception raised, which will cause your +site to display an error page. If you'd like to retrieve a URL without displaying it, you can use a slightly different call:: diff --git a/docs/ref/unicode.txt b/docs/ref/unicode.txt index 7c359da315c..0ef12aecd9e 100644 --- a/docs/ref/unicode.txt +++ b/docs/ref/unicode.txt @@ -290,8 +290,8 @@ Taking care in ``get_absolute_url()`` URLs can only contain ASCII characters. If you're constructing a URL from pieces of data that might be non-ASCII, be careful to encode the results in a -way that is suitable for a URL. The :func:`~django.core.urlresolvers.reverse` -function handles this for you automatically. +way that is suitable for a URL. The :func:`~django.urls.reverse` function +handles this for you automatically. If you're constructing a URL manually (i.e., *not* using the ``reverse()`` function), you'll need to take care of the encoding yourself. In this case, diff --git a/docs/ref/urlresolvers.txt b/docs/ref/urlresolvers.txt index c9a1cf3af3f..11c353502fd 100644 --- a/docs/ref/urlresolvers.txt +++ b/docs/ref/urlresolvers.txt @@ -1,8 +1,14 @@ -============================================== -``django.core.urlresolvers`` utility functions -============================================== +================================= +``django.urls`` utility functions +================================= -.. module:: django.core.urlresolvers +.. module:: django.urls + +.. deprecated:: 1.10 + + In older versions, these functions are located in + ``django.core.urlresolvers``. Importing from the old location will continue + to work until Django 2.0. reverse() --------- @@ -31,7 +37,7 @@ you can use any of the following to reverse the URL:: If the URL accepts arguments, you may pass them in ``args``. For example:: - from django.core.urlresolvers import reverse + from django.urls import reverse def myview(request): return HttpResponseRedirect(reverse('arch-summary', args=[1945])) @@ -44,7 +50,7 @@ You can also pass ``kwargs`` instead of ``args``. For example:: ``args`` and ``kwargs`` cannot be passed to ``reverse()`` at the same time. If no match can be made, ``reverse()`` raises a -:class:`~django.core.urlresolvers.NoReverseMatch` exception. +:class:`~django.urls.NoReverseMatch` exception. The ``reverse()`` function can reverse a large variety of regular expression patterns for URLs, but not every possible one. The main restriction at the @@ -103,13 +109,12 @@ corresponding view functions. It has the following signature: .. function:: resolve(path, urlconf=None) ``path`` is the URL path you want to resolve. As with -:func:`~django.core.urlresolvers.reverse`, you don't need to -worry about the ``urlconf`` parameter. The function returns a -:class:`ResolverMatch` object that allows you -to access various meta-data about the resolved URL. +:func:`~django.urls.reverse`, you don't need to worry about the ``urlconf`` +parameter. The function returns a :class:`ResolverMatch` object that allows you +to access various metadata about the resolved URL. If the URL does not resolve, the function raises a -:exc:`~django.core.urlresolvers.Resolver404` exception (a subclass of +:exc:`~django.urls.Resolver404` exception (a subclass of :class:`~django.http.Http404`) . .. class:: ResolverMatch @@ -175,10 +180,10 @@ A :class:`ResolverMatch` object can also be assigned to a triple:: func, args, kwargs = resolve('/some/path/') -One possible use of :func:`~django.core.urlresolvers.resolve` would be to test -whether a view would raise a ``Http404`` error before redirecting to it:: +One possible use of :func:`~django.urls.resolve` would be to test whether a +view would raise a ``Http404`` error before redirecting to it:: - from django.core.urlresolvers import resolve + from django.urls import resolve from django.http import HttpResponseRedirect, Http404 from django.utils.six.moves.urllib.parse import urlparse @@ -202,12 +207,11 @@ get_script_prefix() .. function:: get_script_prefix() -Normally, you should always use :func:`~django.core.urlresolvers.reverse` to -define URLs within your application. However, if your application constructs -part of the URL hierarchy itself, you may occasionally need to generate URLs. -In that case, you need to be able to find the base URL of the Django project -within its Web server (normally, :func:`~django.core.urlresolvers.reverse` -takes care of this for you). In that case, you can call -``get_script_prefix()``, which will return the script prefix portion of the URL -for your Django project. If your Django project is at the root of its web -server, this is always ``"/"``. +Normally, you should always use :func:`~django.urls.reverse` to define URLs +within your application. However, if your application constructs part of the +URL hierarchy itself, you may occasionally need to generate URLs. In that +case, you need to be able to find the base URL of the Django project within +its Web server (normally, :func:`~django.urls.reverse` takes care of this for +you). In that case, you can call ``get_script_prefix()``, which will return +the script prefix portion of the URL for your Django project. If your Django +project is at the root of its web server, this is always ``"/"``. diff --git a/docs/releases/1.1.txt b/docs/releases/1.1.txt index 11e5ceb0d4f..63b8c9ab131 100644 --- a/docs/releases/1.1.txt +++ b/docs/releases/1.1.txt @@ -380,11 +380,11 @@ Other new features and changes introduced since Django 1.0 include: order to allow fine-grained control of when and where the CSRF processing takes place. -* :func:`~django.core.urlresolvers.reverse` and code which uses it (e.g., the - ``{% url %}`` template tag) now works with URLs in Django's administrative - site, provided that the admin URLs are set up via ``include(admin.site.urls)`` - (sending admin requests to the ``admin.site.root`` view still works, but URLs - in the admin will not be "reversible" when configured this way). +* ``reverse()`` and code which uses it (e.g., the ``{% url %}`` template tag) + now works with URLs in Django's administrative site, provided that the admin + URLs are set up via ``include(admin.site.urls)`` (sending admin requests to + the ``admin.site.root`` view still works, but URLs in the admin will not be + "reversible" when configured this way). * The ``include()`` function in Django URLconf modules can now accept sequences of URL patterns (generated by ``patterns()``) in addition to module names. diff --git a/docs/releases/1.10.txt b/docs/releases/1.10.txt index 34294e901af..3143d9e0cd1 100644 --- a/docs/releases/1.10.txt +++ b/docs/releases/1.10.txt @@ -460,6 +460,9 @@ Miscellaneous * The ``shell --plain`` option is deprecated in favor of ``-i python`` or ``--interface python``. +* Importing from the ``django.core.urlresolvers`` module is deprecated in + favor of its new location, :mod:`django.urls`. + .. _removed-features-1.10: Features removed in 1.10 @@ -485,8 +488,8 @@ removed in Django 1.10 (please see the :ref:`deprecation timeline * Using an incorrect count of unpacked values in the ``for`` template tag raises an exception rather than failing silently. -* The ability to :func:`~django.core.urlresolvers.reverse` URLs using a dotted - Python path is removed. +* The ability to :func:`~django.urls.reverse` URLs using a dotted Python path + is removed. * Support for ``optparse`` is dropped for custom management commands. diff --git a/docs/releases/1.4.11.txt b/docs/releases/1.4.11.txt index d6d65f7789c..990f511e711 100644 --- a/docs/releases/1.4.11.txt +++ b/docs/releases/1.4.11.txt @@ -16,14 +16,12 @@ Django's URL handling is based on a mapping of regex patterns consists of matching a requested URL against those patterns to determine the appropriate view to invoke. -Django also provides a convenience function -- -:func:`~django.core.urlresolvers.reverse` -- which performs this process -in the opposite direction. The ``reverse()`` function takes -information about a view and returns a URL which would invoke that -view. Use of ``reverse()`` is encouraged for application developers, -as the output of ``reverse()`` is always based on the current URL -patterns, meaning developers do not need to change other code when -making changes to URLs. +Django also provides a convenience function -- ``reverse()`` -- which performs +this process in the opposite direction. The ``reverse()`` function takes +information about a view and returns a URL which would invoke that view. Use +of ``reverse()`` is encouraged for application developers, as the output of +``reverse()`` is always based on the current URL patterns, meaning developers +do not need to change other code when making changes to URLs. One argument signature for ``reverse()`` is to pass a dotted Python path to the desired view. In this situation, Django will import the diff --git a/docs/releases/1.4.12.txt b/docs/releases/1.4.12.txt index c15842b3f4e..7c7e65a5772 100644 --- a/docs/releases/1.4.12.txt +++ b/docs/releases/1.4.12.txt @@ -9,6 +9,5 @@ Django 1.4.12 fixes a regression in the 1.4.11 security release. Bugfixes ======== -* Restored the ability to :meth:`~django.core.urlresolvers.reverse` views - created using :func:`functools.partial()` - (:ticket:`22486`) +* Restored the ability to ``reverse()`` views created using + :func:`functools.partial()` (:ticket:`22486`). diff --git a/docs/releases/1.4.14.txt b/docs/releases/1.4.14.txt index d5d1a66f897..5e8f0c16b82 100644 --- a/docs/releases/1.4.14.txt +++ b/docs/releases/1.4.14.txt @@ -6,8 +6,8 @@ Django 1.4.14 release notes Django 1.4.14 fixes several security issues in 1.4.13. -:func:`~django.core.urlresolvers.reverse()` could generate URLs pointing to other hosts -======================================================================================= +``reverse()`` could generate URLs pointing to other hosts +========================================================= In certain situations, URL reversing could generate scheme-relative URLs (URLs starting with two slashes), which could unexpectedly redirect a user to a diff --git a/docs/releases/1.4.txt b/docs/releases/1.4.txt index 9f73a42ae6f..f56db541a7d 100644 --- a/docs/releases/1.4.txt +++ b/docs/releases/1.4.txt @@ -371,8 +371,8 @@ Django 1.4 to store the wizard's state in the user's cookies. ``reverse_lazy`` ~~~~~~~~~~~~~~~~ -A lazily evaluated version of :func:`django.core.urlresolvers.reverse` was -added to allow using URL reversals before the project's URLconf gets loaded. +A lazily evaluated version of ``reverse()`` was added to allow using URL +reversals before the project's URLconf gets loaded. Translating URL patterns ~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/releases/1.5.6.txt b/docs/releases/1.5.6.txt index 7c3cd3a75db..915dd7e22a6 100644 --- a/docs/releases/1.5.6.txt +++ b/docs/releases/1.5.6.txt @@ -15,14 +15,12 @@ Django's URL handling is based on a mapping of regex patterns consists of matching a requested URL against those patterns to determine the appropriate view to invoke. -Django also provides a convenience function -- -:func:`~django.core.urlresolvers.reverse` -- which performs this process -in the opposite direction. The ``reverse()`` function takes -information about a view and returns a URL which would invoke that -view. Use of ``reverse()`` is encouraged for application developers, -as the output of ``reverse()`` is always based on the current URL -patterns, meaning developers do not need to change other code when -making changes to URLs. +Django also provides a convenience function -- ``reverse()`` -- which performs +this process in the opposite direction. The ``reverse()`` function takes +information about a view and returns a URL which would invoke that view. Use +of ``reverse()`` is encouraged for application developers, as the output of +``reverse()`` is always based on the current URL patterns, meaning developers +do not need to change other code when making changes to URLs. One argument signature for ``reverse()`` is to pass a dotted Python path to the desired view. In this situation, Django will import the diff --git a/docs/releases/1.5.7.txt b/docs/releases/1.5.7.txt index a650a9b82d7..30611527d7f 100644 --- a/docs/releases/1.5.7.txt +++ b/docs/releases/1.5.7.txt @@ -9,5 +9,5 @@ Django 1.5.7 fixes a regression in the 1.5.6 security release. Bugfixes ======== -* Restored the ability to :meth:`~django.core.urlresolvers.reverse` views - created using :func:`functools.partial()` (:ticket:`22486`). +* Restored the ability to ``reverse()`` views created using + :func:`functools.partial()` (:ticket:`22486`). diff --git a/docs/releases/1.5.9.txt b/docs/releases/1.5.9.txt index 9942ba081f5..a2ccc165676 100644 --- a/docs/releases/1.5.9.txt +++ b/docs/releases/1.5.9.txt @@ -6,8 +6,8 @@ Django 1.5.9 release notes Django 1.5.9 fixes several security issues in 1.5.8. -:func:`~django.core.urlresolvers.reverse()` could generate URLs pointing to other hosts -======================================================================================= +``reverse()`` could generate URLs pointing to other hosts +========================================================= In certain situations, URL reversing could generate scheme-relative URLs (URLs starting with two slashes), which could unexpectedly redirect a user to a diff --git a/docs/releases/1.5.txt b/docs/releases/1.5.txt index 4b3e286f2b0..9a0b036b040 100644 --- a/docs/releases/1.5.txt +++ b/docs/releases/1.5.txt @@ -293,8 +293,8 @@ Django 1.5 also includes several smaller improvements worth noting: objects fetched into memory. See :meth:`QuerySet.delete() ` for details. -* An instance of :class:`~django.core.urlresolvers.ResolverMatch` is stored on - the request as ``resolver_match``. +* An instance of ``ResolverMatch`` is stored on the request as + ``resolver_match``. * By default, all logging messages reaching the ``django`` logger when :setting:`DEBUG` is ``True`` are sent to the console (unless you redefine the diff --git a/docs/releases/1.6.3.txt b/docs/releases/1.6.3.txt index 80438acb4b2..0cba7d59af9 100644 --- a/docs/releases/1.6.3.txt +++ b/docs/releases/1.6.3.txt @@ -15,14 +15,12 @@ Django's URL handling is based on a mapping of regex patterns consists of matching a requested URL against those patterns to determine the appropriate view to invoke. -Django also provides a convenience function -- -:func:`~django.core.urlresolvers.reverse` -- which performs this process -in the opposite direction. The ``reverse()`` function takes -information about a view and returns a URL which would invoke that -view. Use of ``reverse()`` is encouraged for application developers, -as the output of ``reverse()`` is always based on the current URL -patterns, meaning developers do not need to change other code when -making changes to URLs. +Django also provides a convenience function -- ``reverse()`` -- which performs +this process in the opposite direction. The ``reverse()`` function takes +information about a view and returns a URL which would invoke that view. Use +of ``reverse()`` is encouraged for application developers, as the output of +``reverse()`` is always based on the current URL patterns, meaning developers +do not need to change other code when making changes to URLs. One argument signature for ``reverse()`` is to pass a dotted Python path to the desired view. In this situation, Django will import the diff --git a/docs/releases/1.6.4.txt b/docs/releases/1.6.4.txt index ad927cce74e..bd437650f6d 100644 --- a/docs/releases/1.6.4.txt +++ b/docs/releases/1.6.4.txt @@ -13,10 +13,8 @@ Bugfixes cookie format of Django 1.4 and earlier to facilitate upgrading to 1.6 from 1.4 (:ticket:`22426`). -* Restored the ability to :meth:`~django.core.urlresolvers.reverse` views - created using :func:`functools.partial()` - (:ticket:`22486`). +* Restored the ability to ``reverse()`` views created using + :func:`functools.partial()` (:ticket:`22486`). * Fixed the ``object_id`` of the ``LogEntry`` that's created after a user - password change in the admin - (:ticket:`22515`). + password change in the admin (:ticket:`22515`). diff --git a/docs/releases/1.6.6.txt b/docs/releases/1.6.6.txt index 6081cbc2290..42ad2f8b5f5 100644 --- a/docs/releases/1.6.6.txt +++ b/docs/releases/1.6.6.txt @@ -6,8 +6,8 @@ Django 1.6.6 release notes Django 1.6.6 fixes several security issues and bugs in 1.6.5. -:func:`~django.core.urlresolvers.reverse()` could generate URLs pointing to other hosts -======================================================================================= +``reverse()`` could generate URLs pointing to other hosts +========================================================= In certain situations, URL reversing could generate scheme-relative URLs (URLs starting with two slashes), which could unexpectedly redirect a user to a diff --git a/docs/releases/1.6.txt b/docs/releases/1.6.txt index 23de67ee765..6e031786202 100644 --- a/docs/releases/1.6.txt +++ b/docs/releases/1.6.txt @@ -583,16 +583,17 @@ be at the end of a line. If they are not, the comments are ignored and {{ title }}{# Translators: Extracted and associated with 'Welcome' below #}

{% trans "Welcome" %}

-Quoting in :func:`~django.core.urlresolvers.reverse` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Quoting in ``reverse()`` +~~~~~~~~~~~~~~~~~~~~~~~~ When reversing URLs, Django didn't apply :func:`~django.utils.http.urlquote` to arguments before interpolating them in URL patterns. This bug is fixed in Django 1.6. If you worked around this bug by applying URL quoting before -passing arguments to :func:`~django.core.urlresolvers.reverse`, this may -result in double-quoting. If this happens, simply remove the URL quoting from -your code. You will also have to replace special characters in URLs used in -:func:`~django.test.SimpleTestCase.assertRedirects` with their encoded versions. +passing arguments to ``reverse()``, this may result in double-quoting. If this +happens, simply remove the URL quoting from your code. You will also have to +replace special characters in URLs used in +:func:`~django.test.SimpleTestCase.assertRedirects` with their encoded +versions. Storage of IP addresses in the comments app ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -902,12 +903,12 @@ Miscellaneous stored as ``null``. Previously, storing a ``blank`` value in a field which did not allow ``null`` would cause a database exception at runtime. -* If a :class:`~django.core.urlresolvers.NoReverseMatch` exception is raised - from a method when rendering a template, it is not silenced. For example, - ``{{ obj.view_href }}`` will cause template rendering to fail if - ``view_href()`` raises ``NoReverseMatch``. There is no change to the - :ttag:`{% url %}` tag, it causes template rendering to fail like always - when ``NoReverseMatch`` is raised. +* If a ``NoReverseMatch`` exception is raised from a method when rendering a + template, it is not silenced. For example, ``{{ obj.view_href }}`` will + cause template rendering to fail if ``view_href()`` raises + ``NoReverseMatch``. There is no change to the :ttag:`{% url %}` tag, it + causes template rendering to fail like always when ``NoReverseMatch`` is + raised. * :meth:`django.test.Client.logout` now calls :meth:`django.contrib.auth.logout` which will send the diff --git a/docs/releases/1.7.3.txt b/docs/releases/1.7.3.txt index 2f3c9c7f496..f8e0dc8b815 100644 --- a/docs/releases/1.7.3.txt +++ b/docs/releases/1.7.3.txt @@ -82,8 +82,7 @@ Bugfixes (:ticket:`23815`). * Fixed a crash in the ``django.contrib.auth.redirect_to_login`` view when - passing a :func:`~django.core.urlresolvers.reverse_lazy` result on Python 3 - (:ticket:`24097`). + passing a ``reverse_lazy()`` result on Python 3 (:ticket:`24097`). * Added correct formats for Greek (``el``) (:ticket:`23967`). diff --git a/docs/releases/1.8.txt b/docs/releases/1.8.txt index 4abdf63f590..d91b01df46a 100644 --- a/docs/releases/1.8.txt +++ b/docs/releases/1.8.txt @@ -1112,9 +1112,8 @@ Miscellaneous * The default max size of the Oracle test tablespace has increased from 300M (or 200M, before 1.7.2) to 500M. -* :func:`~django.core.urlresolvers.reverse` and - :func:`~django.core.urlresolvers.reverse_lazy` now return Unicode strings - instead of byte strings. +* ``reverse()`` and ``reverse_lazy()`` now return Unicode strings instead of + byte strings. * The ``CacheClass`` shim has been removed from all cache backends. These aliases were provided for backwards compatibility with Django 1.3. @@ -1334,8 +1333,8 @@ Using an incorrect count of unpacked values in the :ttag:`for` template tag Using an incorrect count of unpacked values in :ttag:`for` tag will raise an exception rather than fail silently in Django 1.10. -Passing a dotted path to :func:`~django.core.urlresolvers.reverse()` and :ttag:`url` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Passing a dotted path to ``reverse()`` and :ttag:`url` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Reversing URLs by Python path is an expensive operation as it causes the path being reversed to be imported. This behavior has also resulted in a diff --git a/docs/releases/1.9.txt b/docs/releases/1.9.txt index 381ea31138d..c01ca94ec48 100644 --- a/docs/releases/1.9.txt +++ b/docs/releases/1.9.txt @@ -1083,12 +1083,10 @@ Miscellaneous :attr:`~django.test.SimpleTestCase.allow_database_queries` class attribute to ``True`` on your test class. -* :attr:`ResolverMatch.app_name - ` was changed to contain - the full namespace path in the case of nested namespaces. For consistency - with :attr:`ResolverMatch.namespace - `, the empty value is now - an empty string instead of ``None``. +* ``ResolverMatch.app_name`` was changed to contain the full namespace path in + the case of nested namespaces. For consistency with + ``ResolverMatch.namespace``, the empty value is now an empty string instead + of ``None``. * For security hardening, session keys must be at least 8 characters. diff --git a/docs/topics/class-based-views/generic-editing.txt b/docs/topics/class-based-views/generic-editing.txt index 9a9c4b7052c..f5fa2ffcdb0 100644 --- a/docs/topics/class-based-views/generic-editing.txt +++ b/docs/topics/class-based-views/generic-editing.txt @@ -98,7 +98,7 @@ First we need to add :meth:`~django.db.models.Model.get_absolute_url()` to our .. snippet:: :filename: models.py - from django.core.urlresolvers import reverse + from django.urls import reverse from django.db import models class Author(models.Model): @@ -115,7 +115,7 @@ here; we don't have to write any logic ourselves: :filename: views.py from django.views.generic.edit import CreateView, UpdateView, DeleteView - from django.core.urlresolvers import reverse_lazy + from django.urls import reverse_lazy from myapp.models import Author class AuthorCreate(CreateView): @@ -131,8 +131,8 @@ here; we don't have to write any logic ourselves: success_url = reverse_lazy('author-list') .. note:: - We have to use :func:`~django.core.urlresolvers.reverse_lazy` here, not - just ``reverse`` as the urls are not loaded when the file is imported. + We have to use :func:`~django.urls.reverse_lazy` here, not just + ``reverse()`` as the urls are not loaded when the file is imported. The ``fields`` attribute works the same way as the ``fields`` attribute on the inner ``Meta`` class on :class:`~django.forms.ModelForm`. Unless you define the diff --git a/docs/topics/class-based-views/mixins.txt b/docs/topics/class-based-views/mixins.txt index 1dd8b04bb56..5212381a657 100644 --- a/docs/topics/class-based-views/mixins.txt +++ b/docs/topics/class-based-views/mixins.txt @@ -225,7 +225,7 @@ We'll demonstrate this with the ``Author`` model we used in the :filename: views.py from django.http import HttpResponseForbidden, HttpResponseRedirect - from django.core.urlresolvers import reverse + from django.urls import reverse from django.views.generic import View from django.views.generic.detail import SingleObjectMixin from books.models import Author @@ -445,7 +445,7 @@ Our new ``AuthorDetail`` looks like this:: from django import forms from django.http import HttpResponseForbidden - from django.core.urlresolvers import reverse + from django.urls import reverse from django.views.generic import DetailView from django.views.generic.edit import FormMixin from books.models import Author @@ -541,7 +541,7 @@ can find the author we're talking about, and we have to remember to set ``template_name`` to ensure that form errors will render the same template as ``AuthorDisplay`` is using on ``GET``:: - from django.core.urlresolvers import reverse + from django.urls import reverse from django.http import HttpResponseForbidden from django.views.generic import FormView from django.views.generic.detail import SingleObjectMixin diff --git a/docs/topics/http/shortcuts.txt b/docs/topics/http/shortcuts.txt index 8c115145071..7faef877661 100644 --- a/docs/topics/http/shortcuts.txt +++ b/docs/topics/http/shortcuts.txt @@ -103,9 +103,8 @@ This example is equivalent to:: * A model: the model's :meth:`~django.db.models.Model.get_absolute_url()` function will be called. - * A view name, possibly with arguments: :func:`urlresolvers.reverse - ` will be used to reverse-resolve the - name. + * A view name, possibly with arguments: :func:`~django.urls.reverse` will be + used to reverse-resolve the name. * An absolute or relative URL, which will be used as-is for the redirect location. @@ -131,7 +130,7 @@ You can use the :func:`redirect` function in a number of ways. 2. By passing the name of a view and optionally some positional or keyword arguments; the URL will be reverse resolved using the - :func:`~django.core.urlresolvers.reverse` method:: + :func:`~django.urls.reverse` method:: def my_view(request): ... diff --git a/docs/topics/http/urls.txt b/docs/topics/http/urls.txt index 2ec66de53fc..068b91bf60d 100644 --- a/docs/topics/http/urls.txt +++ b/docs/topics/http/urls.txt @@ -533,8 +533,7 @@ layers where URLs are needed: * In templates: Using the :ttag:`url` template tag. -* In Python code: Using the :func:`django.core.urlresolvers.reverse` - function. +* In Python code: Using the :func:`~django.urls.reverse` function. * In higher level code related to handling of URLs of Django model instances: The :meth:`~django.db.models.Model.get_absolute_url` method. @@ -571,7 +570,7 @@ You can obtain these in template code by using: Or in Python code:: - from django.core.urlresolvers import reverse + from django.urls import reverse from django.http import HttpResponseRedirect def redirect_to_year(request): @@ -671,8 +670,8 @@ the fully qualified name into parts and then tries the following lookup: 2. If there is a current application defined, Django finds and returns the URL resolver for that instance. The current application can be specified with - the ``current_app`` argument to the - :func:`~django.core.urlresolvers.reverse()` function. + the ``current_app`` argument to the :func:`~django.urls.reverse()` + function. The :ttag:`url` template tag uses the namespace of the currently resolved view as the current application in a diff --git a/docs/topics/i18n/translation.txt b/docs/topics/i18n/translation.txt index 9057cebadec..a73a15da4ce 100644 --- a/docs/topics/i18n/translation.txt +++ b/docs/topics/i18n/translation.txt @@ -1360,7 +1360,7 @@ After defining these URL patterns, Django will automatically add the language prefix to the URL patterns that were added by the ``i18n_patterns`` function. Example:: - from django.core.urlresolvers import reverse + from django.urls import reverse from django.utils.translation import activate >>> activate('en') @@ -1414,11 +1414,10 @@ URL patterns can also be marked translatable using the url(_(r'^news/'), include(news_patterns, namespace='news')), ) -After you've created the translations, the -:func:`~django.core.urlresolvers.reverse` function will return the URL in the -active language. Example:: +After you've created the translations, the :func:`~django.urls.reverse` +function will return the URL in the active language. Example:: - from django.core.urlresolvers import reverse + from django.urls import reverse from django.utils.translation import activate >>> activate('en') diff --git a/docs/topics/templates.txt b/docs/topics/templates.txt index e8304344687..3aaa017a255 100644 --- a/docs/topics/templates.txt +++ b/docs/topics/templates.txt @@ -411,7 +411,7 @@ For example, you can create ``myproject/jinja2.py`` with this content:: from __future__ import absolute_import # Python 2 only from django.contrib.staticfiles.storage import staticfiles_storage - from django.core.urlresolvers import reverse + from django.urls import reverse from jinja2 import Environment diff --git a/docs/topics/testing/tools.txt b/docs/topics/testing/tools.txt index f9e302a0c11..d66f8df5ba3 100644 --- a/docs/topics/testing/tools.txt +++ b/docs/topics/testing/tools.txt @@ -507,9 +507,8 @@ Specifically, a ``Response`` object has the following attributes: .. attribute:: resolver_match - An instance of :class:`~django.core.urlresolvers.ResolverMatch` for the - response. You can use the - :attr:`~django.core.urlresolvers.ResolverMatch.func` attribute, for + An instance of :class:`~django.urls.ResolverMatch` for the response. + You can use the :attr:`~django.urls.ResolverMatch.func` attribute, for example, to verify the view that served the response:: # my_view here is a function based view @@ -520,7 +519,7 @@ Specifically, a ``Response`` object has the following attributes: self.assertEqual(response.resolver_match.func.__name__, MyView.as_view().__name__) If the given URL is not found, accessing this attribute will raise a - :exc:`~django.core.urlresolvers.Resolver404` exception. + :exc:`~django.urls.Resolver404` exception. You can also use dictionary syntax on the response object to query the value of any settings in the HTTP headers. For example, you could determine the diff --git a/tests/admin_changelist/tests.py b/tests/admin_changelist/tests.py index 81bf3bdfc6d..f0f5fdc4ae4 100644 --- a/tests/admin_changelist/tests.py +++ b/tests/admin_changelist/tests.py @@ -10,10 +10,10 @@ from django.contrib.admin.tests import AdminSeleniumWebDriverTestCase from django.contrib.admin.views.main import ALL_VAR, SEARCH_VAR, ChangeList from django.contrib.auth.models import User from django.contrib.contenttypes.models import ContentType -from django.core.urlresolvers import reverse from django.template import Context, Template from django.test import TestCase, ignore_warnings, override_settings from django.test.client import RequestFactory +from django.urls import reverse from django.utils import formats, six from django.utils.deprecation import RemovedInDjango20Warning diff --git a/tests/admin_custom_urls/models.py b/tests/admin_custom_urls/models.py index 5a1b2f5c2e2..5365ee34bda 100644 --- a/tests/admin_custom_urls/models.py +++ b/tests/admin_custom_urls/models.py @@ -1,9 +1,9 @@ from functools import update_wrapper from django.contrib import admin -from django.core.urlresolvers import reverse from django.db import models from django.http import HttpResponseRedirect +from django.urls import reverse from django.utils.encoding import python_2_unicode_compatible diff --git a/tests/admin_custom_urls/tests.py b/tests/admin_custom_urls/tests.py index 5e9a119b6a0..11690e0757f 100644 --- a/tests/admin_custom_urls/tests.py +++ b/tests/admin_custom_urls/tests.py @@ -4,9 +4,9 @@ import datetime from django.contrib.admin.utils import quote from django.contrib.auth.models import User -from django.core.urlresolvers import reverse from django.template.response import TemplateResponse from django.test import TestCase, override_settings +from django.urls import reverse from .models import Action, Car, Person diff --git a/tests/admin_docs/tests.py b/tests/admin_docs/tests.py index 4b8707adcd1..f651a44ddda 100644 --- a/tests/admin_docs/tests.py +++ b/tests/admin_docs/tests.py @@ -7,9 +7,9 @@ from django.contrib.admindocs import utils from django.contrib.admindocs.views import get_return_data_type from django.contrib.auth.models import User from django.contrib.sites.models import Site -from django.core.urlresolvers import reverse from django.test import TestCase, modify_settings, override_settings from django.test.utils import captured_stderr +from django.urls import reverse from .models import Company, Person diff --git a/tests/admin_inlines/tests.py b/tests/admin_inlines/tests.py index 0a502023405..0f8ad4bc250 100644 --- a/tests/admin_inlines/tests.py +++ b/tests/admin_inlines/tests.py @@ -7,8 +7,8 @@ from django.contrib.admin.helpers import InlineAdminForm from django.contrib.admin.tests import AdminSeleniumWebDriverTestCase from django.contrib.auth.models import Permission, User from django.contrib.contenttypes.models import ContentType -from django.core.urlresolvers import reverse from django.test import RequestFactory, TestCase, override_settings +from django.urls import reverse from .admin import InnerInline, site as admin_site from .models import ( diff --git a/tests/admin_utils/test_logentry.py b/tests/admin_utils/test_logentry.py index 6943e094e7c..70f0de7595d 100644 --- a/tests/admin_utils/test_logentry.py +++ b/tests/admin_utils/test_logentry.py @@ -6,8 +6,8 @@ from django.contrib.admin.models import ADDITION, CHANGE, DELETION, LogEntry from django.contrib.admin.utils import quote from django.contrib.auth.models import User from django.contrib.contenttypes.models import ContentType -from django.core.urlresolvers import reverse from django.test import TestCase, override_settings +from django.urls import reverse from django.utils import six from django.utils.encoding import force_bytes from django.utils.html import escape diff --git a/tests/admin_views/test_adminsite.py b/tests/admin_views/test_adminsite.py index dd6944ccede..97d11b69393 100644 --- a/tests/admin_views/test_adminsite.py +++ b/tests/admin_views/test_adminsite.py @@ -5,9 +5,9 @@ import datetime from django.conf.urls import url from django.contrib import admin from django.contrib.auth.models import User -from django.core.urlresolvers import reverse from django.test import TestCase, override_settings from django.test.client import RequestFactory +from django.urls import reverse from .models import Article diff --git a/tests/admin_views/tests.py b/tests/admin_views/tests.py index 9bf0c2dc8eb..b07b1a7bb71 100644 --- a/tests/admin_views/tests.py +++ b/tests/admin_views/tests.py @@ -23,7 +23,6 @@ from django.contrib.staticfiles.storage import staticfiles_storage from django.core import mail from django.core.checks import Error from django.core.files import temp as tempfile -from django.core.urlresolvers import NoReverseMatch, resolve, reverse from django.forms.utils import ErrorList from django.template.loader import render_to_string from django.template.response import TemplateResponse @@ -32,6 +31,7 @@ from django.test import ( override_settings, skipUnlessDBFeature, ) from django.test.utils import override_script_prefix, patch_logger +from django.urls import NoReverseMatch, resolve, reverse from django.utils import formats, six, translation from django.utils._os import upath from django.utils.cache import get_max_age diff --git a/tests/admin_widgets/tests.py b/tests/admin_widgets/tests.py index 1fe4741125b..d1966a13648 100644 --- a/tests/admin_widgets/tests.py +++ b/tests/admin_widgets/tests.py @@ -15,9 +15,9 @@ from django.contrib.admin.tests import AdminSeleniumWebDriverTestCase from django.contrib.auth.models import User from django.core.files.storage import default_storage from django.core.files.uploadedfile import SimpleUploadedFile -from django.core.urlresolvers import reverse from django.db.models import CharField, DateField from django.test import SimpleTestCase, TestCase, override_settings +from django.urls import reverse from django.utils import six, translation from . import models diff --git a/tests/auth_tests/test_views.py b/tests/auth_tests/test_views.py index 86c2accaecc..3921cb39638 100644 --- a/tests/auth_tests/test_views.py +++ b/tests/auth_tests/test_views.py @@ -20,12 +20,12 @@ from django.contrib.auth.views import login as login_view, redirect_to_login from django.contrib.sessions.middleware import SessionMiddleware from django.contrib.sites.requests import RequestSite from django.core import mail -from django.core.urlresolvers import NoReverseMatch, reverse, reverse_lazy from django.db import connection from django.http import HttpRequest, QueryDict from django.middleware.csrf import CsrfViewMiddleware, get_token from django.test import TestCase, override_settings from django.test.utils import patch_logger +from django.urls import NoReverseMatch, reverse, reverse_lazy from django.utils.encoding import force_text from django.utils.http import urlquote from django.utils.six.moves.urllib.parse import ParseResult, urlparse diff --git a/tests/forms_tests/tests/test_widgets.py b/tests/forms_tests/tests/test_widgets.py index 114f824eb9b..965f8f5493a 100644 --- a/tests/forms_tests/tests/test_widgets.py +++ b/tests/forms_tests/tests/test_widgets.py @@ -2,7 +2,6 @@ from __future__ import unicode_literals from django.contrib.admin.tests import AdminSeleniumWebDriverTestCase -from django.core.urlresolvers import reverse from django.forms import ( CheckboxSelectMultiple, ClearableFileInput, RadioSelect, TextInput, ) @@ -10,6 +9,7 @@ from django.forms.widgets import ( ChoiceFieldRenderer, ChoiceInput, RadioFieldRenderer, ) from django.test import SimpleTestCase, override_settings +from django.urls import reverse from django.utils import six from django.utils.encoding import force_text, python_2_unicode_compatible from django.utils.safestring import SafeData diff --git a/tests/generic_inline_admin/tests.py b/tests/generic_inline_admin/tests.py index a491943c4fd..b2b88460631 100644 --- a/tests/generic_inline_admin/tests.py +++ b/tests/generic_inline_admin/tests.py @@ -9,12 +9,12 @@ from django.contrib.auth.models import User from django.contrib.contenttypes.admin import GenericTabularInline from django.contrib.contenttypes.forms import generic_inlineformset_factory from django.contrib.contenttypes.models import ContentType -from django.core.urlresolvers import reverse from django.forms.formsets import DEFAULT_MAX_NUM from django.forms.models import ModelForm from django.test import ( RequestFactory, SimpleTestCase, TestCase, override_settings, ) +from django.urls import reverse from .admin import MediaInline, MediaPermanentInline, site as admin_site from .models import Category, Episode, EpisodePermanent, Media, PhoneNumber diff --git a/tests/generic_views/models.py b/tests/generic_views/models.py index d27a2fa9126..afe4d042cd0 100644 --- a/tests/generic_views/models.py +++ b/tests/generic_views/models.py @@ -1,7 +1,7 @@ -from django.core.urlresolvers import reverse from django.db import models from django.db.models import QuerySet from django.db.models.manager import BaseManager +from django.urls import reverse from django.utils.encoding import python_2_unicode_compatible diff --git a/tests/generic_views/test_base.py b/tests/generic_views/test_base.py index 6d5045a81b5..72c813b2048 100644 --- a/tests/generic_views/test_base.py +++ b/tests/generic_views/test_base.py @@ -4,10 +4,10 @@ import time import unittest from django.core.exceptions import ImproperlyConfigured -from django.core.urlresolvers import resolve from django.http import HttpResponse from django.test import RequestFactory, SimpleTestCase, override_settings from django.test.utils import require_jinja2 +from django.urls import resolve from django.views.generic import RedirectView, TemplateView, View from . import views diff --git a/tests/generic_views/test_edit.py b/tests/generic_views/test_edit.py index ba855f3cd1d..b4b2986eeda 100644 --- a/tests/generic_views/test_edit.py +++ b/tests/generic_views/test_edit.py @@ -2,9 +2,9 @@ from __future__ import unicode_literals from django import forms from django.core.exceptions import ImproperlyConfigured -from django.core.urlresolvers import reverse from django.test import SimpleTestCase, TestCase, override_settings from django.test.client import RequestFactory +from django.urls import reverse from django.views.generic.base import View from django.views.generic.edit import CreateView, FormMixin, ModelFormMixin diff --git a/tests/generic_views/views.py b/tests/generic_views/views.py index 11e1ec3b4d2..e46bc091434 100644 --- a/tests/generic_views/views.py +++ b/tests/generic_views/views.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals from django.contrib.auth.decorators import login_required from django.core.paginator import Paginator -from django.core.urlresolvers import reverse, reverse_lazy +from django.urls import reverse, reverse_lazy from django.utils.decorators import method_decorator from django.views import generic diff --git a/tests/i18n/patterns/tests.py b/tests/i18n/patterns/tests.py index 1e8bb9d9298..284bb697a49 100644 --- a/tests/i18n/patterns/tests.py +++ b/tests/i18n/patterns/tests.py @@ -3,12 +3,12 @@ from __future__ import unicode_literals import os from django.core.exceptions import ImproperlyConfigured -from django.core.urlresolvers import clear_url_caches, reverse, translate_url from django.http import HttpResponsePermanentRedirect from django.middleware.locale import LocaleMiddleware from django.template import Context, Template from django.test import SimpleTestCase, override_settings from django.test.utils import override_script_prefix +from django.urls import clear_url_caches, reverse, translate_url from django.utils import translation from django.utils._os import upath diff --git a/tests/messages_tests/base.py b/tests/messages_tests/base.py index ad2d77cafb0..e3d0e6d8d41 100644 --- a/tests/messages_tests/base.py +++ b/tests/messages_tests/base.py @@ -4,8 +4,8 @@ from django.contrib.messages.api import MessageFailure from django.contrib.messages.constants import DEFAULT_LEVELS from django.contrib.messages.storage import base, default_storage from django.contrib.messages.storage.base import Message -from django.core.urlresolvers import reverse from django.test import modify_settings, override_settings +from django.urls import reverse from django.utils.translation import ugettext_lazy diff --git a/tests/messages_tests/test_mixins.py b/tests/messages_tests/test_mixins.py index f59de5abd94..2b8b504a314 100644 --- a/tests/messages_tests/test_mixins.py +++ b/tests/messages_tests/test_mixins.py @@ -1,5 +1,5 @@ -from django.core.urlresolvers import reverse from django.test import SimpleTestCase, override_settings +from django.urls import reverse from .urls import ContactFormViewWithMsg diff --git a/tests/messages_tests/urls.py b/tests/messages_tests/urls.py index d7486900447..4245b3fcee6 100644 --- a/tests/messages_tests/urls.py +++ b/tests/messages_tests/urls.py @@ -2,10 +2,10 @@ from django import forms from django.conf.urls import url from django.contrib import messages from django.contrib.messages.views import SuccessMessageMixin -from django.core.urlresolvers import reverse from django.http import HttpResponse, HttpResponseRedirect from django.template import engines from django.template.response import TemplateResponse +from django.urls import reverse from django.views.decorators.cache import never_cache from django.views.generic.edit import FormView diff --git a/tests/proxy_models/tests.py b/tests/proxy_models/tests.py index e384e66c8a0..625925fcb5a 100644 --- a/tests/proxy_models/tests.py +++ b/tests/proxy_models/tests.py @@ -7,10 +7,10 @@ from django.contrib import admin from django.contrib.auth.models import User as AuthUser from django.contrib.contenttypes.models import ContentType from django.core import checks, management -from django.core.urlresolvers import reverse from django.db import DEFAULT_DB_ALIAS, models from django.db.models import signals from django.test import TestCase, override_settings +from django.urls import reverse from .admin import admin as force_admin_model_registration # NOQA from .models import ( diff --git a/tests/resolve_url/tests.py b/tests/resolve_url/tests.py index 52e800a8c0e..b909f68abee 100644 --- a/tests/resolve_url/tests.py +++ b/tests/resolve_url/tests.py @@ -1,9 +1,9 @@ from __future__ import unicode_literals from django.contrib.auth.views import logout -from django.core.urlresolvers import NoReverseMatch, reverse_lazy from django.shortcuts import resolve_url from django.test import SimpleTestCase, override_settings +from django.urls import NoReverseMatch, reverse_lazy from django.utils import six from .models import UnimportantThing diff --git a/tests/sitemaps_tests/models.py b/tests/sitemaps_tests/models.py index c155c848ba3..29b3e8cde71 100644 --- a/tests/sitemaps_tests/models.py +++ b/tests/sitemaps_tests/models.py @@ -1,5 +1,5 @@ -from django.core.urlresolvers import reverse from django.db import models +from django.urls import reverse class TestModel(models.Model): diff --git a/tests/template_tests/syntax_tests/test_url.py b/tests/template_tests/syntax_tests/test_url.py index 53b3f255185..e6e20d08f36 100644 --- a/tests/template_tests/syntax_tests/test_url.py +++ b/tests/template_tests/syntax_tests/test_url.py @@ -1,7 +1,7 @@ # coding: utf-8 -from django.core.urlresolvers import NoReverseMatch, resolve from django.template import RequestContext, TemplateSyntaxError from django.test import RequestFactory, SimpleTestCase, override_settings +from django.urls import NoReverseMatch, resolve from ..utils import setup diff --git a/tests/template_tests/tests.py b/tests/template_tests/tests.py index 38d7eaa8bb9..43cc1820089 100644 --- a/tests/template_tests/tests.py +++ b/tests/template_tests/tests.py @@ -4,10 +4,10 @@ from __future__ import unicode_literals import sys from django.contrib.auth.models import Group -from django.core import urlresolvers from django.template import Context, Engine, TemplateSyntaxError from django.template.base import UNKNOWN_SOURCE from django.test import SimpleTestCase, override_settings +from django.urls import NoReverseMatch class TemplateTests(SimpleTestCase): @@ -26,7 +26,7 @@ class TemplateTests(SimpleTestCase): """ t = Engine(debug=True).from_string('{% url will_not_match %}') c = Context() - with self.assertRaises(urlresolvers.NoReverseMatch): + with self.assertRaises(NoReverseMatch): t.render(c) def test_url_reverse_view_name(self): @@ -38,7 +38,7 @@ class TemplateTests(SimpleTestCase): c = Context() try: t.render(c) - except urlresolvers.NoReverseMatch: + except NoReverseMatch: tb = sys.exc_info()[2] depth = 0 while tb.tb_next is not None: @@ -118,7 +118,7 @@ class TemplateTests(SimpleTestCase): """ engine = Engine(app_dirs=True) t = engine.get_template('included_content.html') - with self.assertRaises(urlresolvers.NoReverseMatch): + with self.assertRaises(NoReverseMatch): t.render(Context()) def test_debug_tag_non_ascii(self): diff --git a/tests/test_client/tests.py b/tests/test_client/tests.py index cd3b6d77f9d..c47107a6761 100644 --- a/tests/test_client/tests.py +++ b/tests/test_client/tests.py @@ -26,11 +26,11 @@ import datetime from django.contrib.auth.models import User from django.core import mail -from django.core.urlresolvers import reverse_lazy from django.http import HttpResponse from django.test import ( Client, RequestFactory, SimpleTestCase, TestCase, override_settings, ) +from django.urls import reverse_lazy from .views import get_view, post_view, trace_view diff --git a/tests/test_client_regress/tests.py b/tests/test_client_regress/tests.py index d12087078ad..bb078a1ff8f 100644 --- a/tests/test_client_regress/tests.py +++ b/tests/test_client_regress/tests.py @@ -10,7 +10,6 @@ import os from django.contrib.auth.models import User from django.contrib.auth.signals import user_logged_in, user_logged_out -from django.core.urlresolvers import NoReverseMatch, reverse from django.http import HttpResponse from django.template import ( Context, RequestContext, TemplateSyntaxError, engines, @@ -21,6 +20,7 @@ from django.test import ( ) from django.test.client import RedirectCycleError, RequestFactory, encode_file from django.test.utils import ContextList, str_prefix +from django.urls import NoReverseMatch, reverse from django.utils._os import upath from django.utils.deprecation import RemovedInDjango20Warning from django.utils.translation import ugettext_lazy diff --git a/tests/test_utils/tests.py b/tests/test_utils/tests.py index a495ff1010d..e6685a2272e 100644 --- a/tests/test_utils/tests.py +++ b/tests/test_utils/tests.py @@ -8,7 +8,6 @@ from django.conf.urls import url from django.contrib.staticfiles.finders import get_finder, get_finders from django.contrib.staticfiles.storage import staticfiles_storage from django.core.files.storage import default_storage -from django.core.urlresolvers import NoReverseMatch, reverse from django.db import connection, router from django.forms import EmailField, IntegerField from django.http import HttpResponse @@ -19,6 +18,7 @@ from django.test import ( ) from django.test.html import HTMLParseError, parse_html from django.test.utils import CaptureQueriesContext, override_settings +from django.urls import NoReverseMatch, reverse from django.utils import six from django.utils._os import abspathu from django.utils.deprecation import RemovedInDjango20Warning diff --git a/tests/timezones/tests.py b/tests/timezones/tests.py index e482c84df33..97af0e729dd 100644 --- a/tests/timezones/tests.py +++ b/tests/timezones/tests.py @@ -11,7 +11,6 @@ from xml.dom.minidom import parseString from django.contrib.auth.models import User from django.core import serializers from django.core.exceptions import ImproperlyConfigured -from django.core.urlresolvers import reverse from django.db import connection, connections from django.db.models import Max, Min from django.http import HttpRequest @@ -23,6 +22,7 @@ from django.test import ( skipIfDBFeature, skipUnlessDBFeature, ) from django.test.utils import requires_tz_support +from django.urls import reverse from django.utils import six, timezone from .forms import ( diff --git a/tests/urlpatterns_reverse/middleware.py b/tests/urlpatterns_reverse/middleware.py index cc80f141451..13c3d104b66 100644 --- a/tests/urlpatterns_reverse/middleware.py +++ b/tests/urlpatterns_reverse/middleware.py @@ -1,5 +1,5 @@ -from django.core.urlresolvers import reverse from django.http import HttpResponse, StreamingHttpResponse +from django.urls import reverse from . import urlconf_inner diff --git a/tests/urlpatterns_reverse/tests.py b/tests/urlpatterns_reverse/tests.py index da2fc86b8b4..42cbe500913 100644 --- a/tests/urlpatterns_reverse/tests.py +++ b/tests/urlpatterns_reverse/tests.py @@ -13,10 +13,6 @@ from django.conf import settings from django.conf.urls import include, url from django.contrib.auth.models import User from django.core.exceptions import ImproperlyConfigured, ViewDoesNotExist -from django.core.urlresolvers import ( - NoReverseMatch, RegexURLPattern, RegexURLResolver, Resolver404, - ResolverMatch, get_callable, get_resolver, resolve, reverse, reverse_lazy, -) from django.http import ( HttpRequest, HttpResponsePermanentRedirect, HttpResponseRedirect, ) @@ -25,6 +21,10 @@ from django.test import ( SimpleTestCase, TestCase, ignore_warnings, override_settings, ) from django.test.utils import override_script_prefix +from django.urls import ( + NoReverseMatch, RegexURLPattern, RegexURLResolver, Resolver404, + ResolverMatch, get_callable, get_resolver, resolve, reverse, reverse_lazy, +) from django.utils import six from django.utils.deprecation import RemovedInDjango20Warning @@ -458,7 +458,7 @@ class ReverseLazySettingsTest(AdminScriptTestCase): """ def setUp(self): self.write_settings('settings.py', extra=""" -from django.core.urlresolvers import reverse_lazy +from django.urls import reverse_lazy LOGIN_URL = reverse_lazy('login')""") def tearDown(self): diff --git a/tests/urlpatterns_reverse/views.py b/tests/urlpatterns_reverse/views.py index 6fd1c53a00e..6bf5c453d15 100644 --- a/tests/urlpatterns_reverse/views.py +++ b/tests/urlpatterns_reverse/views.py @@ -1,8 +1,8 @@ from functools import partial, update_wrapper from django.contrib.auth.decorators import user_passes_test -from django.core.urlresolvers import reverse_lazy from django.http import HttpResponse +from django.urls import reverse_lazy from django.views.generic import RedirectView diff --git a/tests/urlpatterns_reverse/views_broken.py b/tests/urlpatterns_reverse/views_broken.py index 4953aab2397..2bc60c4b98a 100644 --- a/tests/urlpatterns_reverse/views_broken.py +++ b/tests/urlpatterns_reverse/views_broken.py @@ -1,2 +1,2 @@ # I just raise an AttributeError to confuse the view loading mechanism -raise AttributeError('I am here to confuse django.core.urlresolvers.get_callable') +raise AttributeError('I am here to confuse django.urls.get_callable') diff --git a/tests/user_commands/management/commands/reverse_url.py b/tests/user_commands/management/commands/reverse_url.py index f2064bf05d8..2f88eda62ea 100644 --- a/tests/user_commands/management/commands/reverse_url.py +++ b/tests/user_commands/management/commands/reverse_url.py @@ -1,5 +1,5 @@ from django.core.management.base import BaseCommand -from django.core.urlresolvers import reverse +from django.urls import reverse class Command(BaseCommand): diff --git a/tests/view_tests/tests/test_debug.py b/tests/view_tests/tests/test_debug.py index fc7bd431605..6d904cecbc0 100644 --- a/tests/view_tests/tests/test_debug.py +++ b/tests/view_tests/tests/test_debug.py @@ -13,11 +13,11 @@ from unittest import skipIf from django.core import mail from django.core.files.uploadedfile import SimpleUploadedFile -from django.core.urlresolvers import reverse from django.db import DatabaseError, connection from django.template import TemplateDoesNotExist from django.test import RequestFactory, SimpleTestCase, override_settings from django.test.utils import LoggingCaptureMixin +from django.urls import reverse from django.utils import six from django.utils.encoding import force_bytes, force_text from django.utils.functional import SimpleLazyObject diff --git a/tests/view_tests/tests/test_i18n.py b/tests/view_tests/tests/test_i18n.py index a586a7f66a3..fc0dd883267 100644 --- a/tests/view_tests/tests/test_i18n.py +++ b/tests/view_tests/tests/test_i18n.py @@ -8,11 +8,11 @@ import unittest from os import path from django.conf import settings -from django.core.urlresolvers import reverse from django.test import ( LiveServerTestCase, SimpleTestCase, TestCase, modify_settings, override_settings, ) +from django.urls import reverse from django.utils import six from django.utils._os import upath from django.utils.module_loading import import_string diff --git a/tests/view_tests/views.py b/tests/view_tests/views.py index a30aa21c0de..f80c7ec76a8 100644 --- a/tests/view_tests/views.py +++ b/tests/view_tests/views.py @@ -6,10 +6,10 @@ import logging import sys from django.core.exceptions import PermissionDenied, SuspiciousOperation -from django.core.urlresolvers import get_resolver from django.http import Http404, HttpResponse, JsonResponse from django.shortcuts import render from django.template import TemplateDoesNotExist +from django.urls import get_resolver from django.views.debug import ( SafeExceptionReporterFilter, technical_500_response, )