Refs #28593 -- Moved django.conf.urls.include() to django.urls().

The old location remains for backwards compatibility. Documentation
will be updated separately along with the rest of the URL routing changes.
This commit is contained in:
Tim Graham 2017-09-13 15:24:12 -04:00
parent 0214f367bc
commit ee4043f735
5 changed files with 106 additions and 94 deletions

View File

@ -1,9 +1,4 @@
from importlib import import_module
from django.core.exceptions import ImproperlyConfigured
from django.urls import (
LocaleRegexURLResolver, RegexURLPattern, RegexURLResolver,
)
from django.urls import RegexURLPattern, RegexURLResolver, include
from django.views import defaults
__all__ = ['handler400', 'handler403', 'handler404', 'handler500', 'include', 'url']
@ -14,54 +9,6 @@ handler404 = defaults.page_not_found
handler500 = defaults.server_error
def include(arg, namespace=None):
app_name = None
if isinstance(arg, tuple):
# callable returning a namespace hint
try:
urlconf_module, app_name = arg
except ValueError:
if namespace:
raise ImproperlyConfigured(
'Cannot override the namespace for a dynamic module that '
'provides a namespace.'
)
raise ImproperlyConfigured(
'Passing a %d-tuple to django.conf.urls.include() is not supported. '
'Pass a 2-tuple containing the list of patterns and app_name, '
'and provide the namespace argument to include() instead.' % len(arg)
)
else:
# No namespace hint - use manually provided namespace
urlconf_module = arg
if isinstance(urlconf_module, str):
urlconf_module = import_module(urlconf_module)
patterns = getattr(urlconf_module, 'urlpatterns', urlconf_module)
app_name = getattr(urlconf_module, 'app_name', app_name)
if namespace and not app_name:
raise ImproperlyConfigured(
'Specifying a namespace in django.conf.urls.include() without '
'providing an app_name is not supported. Set the app_name attribute '
'in the included module, or pass a 2-tuple containing the list of '
'patterns and app_name instead.',
)
namespace = namespace or app_name
# Make sure we can iterate through the patterns (without this, some
# testcases will break).
if isinstance(patterns, (list, tuple)):
for url_pattern in patterns:
# Test if the LocaleRegexURLResolver is used within the include;
# this should throw an error since this is not allowed!
if isinstance(url_pattern, LocaleRegexURLResolver):
raise ImproperlyConfigured(
'Using i18n_patterns in an included URLconf is not allowed.')
return (urlconf_module, app_name, namespace)
def url(regex, view, kwargs=None, name=None):
if isinstance(view, (list, tuple)):
# For include(...) processing.

View File

@ -3,6 +3,7 @@ from .base import (
is_valid_path, resolve, reverse, reverse_lazy, set_script_prefix,
set_urlconf, translate_url,
)
from .conf import include
from .exceptions import NoReverseMatch, Resolver404
from .resolvers import (
LocaleRegexProvider, LocaleRegexURLResolver, RegexURLPattern,
@ -15,6 +16,6 @@ __all__ = [
'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',
'include', 'is_valid_path', 'resolve', 'reverse', 'reverse_lazy',
'set_script_prefix', 'set_urlconf', 'translate_url',
]

50
django/urls/conf.py Normal file
View File

@ -0,0 +1,50 @@
"""Functions for use in URLsconfs."""
from importlib import import_module
from django.core.exceptions import ImproperlyConfigured
from .resolvers import LocaleRegexURLResolver
def include(arg, namespace=None):
app_name = None
if isinstance(arg, tuple):
# Callable returning a namespace hint.
try:
urlconf_module, app_name = arg
except ValueError:
if namespace:
raise ImproperlyConfigured(
'Cannot override the namespace for a dynamic module that '
'provides a namespace.'
)
raise ImproperlyConfigured(
'Passing a %d-tuple to include() is not supported. Pass a '
'2-tuple containing the list of patterns and app_name, and '
'provide the namespace argument to include() instead.' % len(arg)
)
else:
# No namespace hint - use manually provided namespace.
urlconf_module = arg
if isinstance(urlconf_module, str):
urlconf_module = import_module(urlconf_module)
patterns = getattr(urlconf_module, 'urlpatterns', urlconf_module)
app_name = getattr(urlconf_module, 'app_name', app_name)
if namespace and not app_name:
raise ImproperlyConfigured(
'Specifying a namespace in include() without providing an app_name '
'is not supported. Set the app_name attribute in the included '
'module, or pass a 2-tuple containing the list of patterns and '
'app_name instead.',
)
namespace = namespace or app_name
# Make sure the patterns can be iterated through (without this, some
# testcases will break).
if isinstance(patterns, (list, tuple)):
for url_pattern in patterns:
if isinstance(url_pattern, LocaleRegexURLResolver):
raise ImproperlyConfigured(
'Using i18n_patterns in an included URLconf is not allowed.'
)
return (urlconf_module, app_name, namespace)

View File

@ -1,6 +1,51 @@
======================================
``django.conf.urls`` utility functions
======================================
=============================================
``django.urls`` functions for use in URLconfs
=============================================
.. module:: django.urls.conf
:synopsis: Functions for use in URLconfs.
.. currentmodule:: django.conf.urls
``include()``
=============
.. function:: include(module, namespace=None)
include(pattern_list)
include((pattern_list, app_namespace), namespace=None)
A function that takes a full Python import path to another URLconf module
that should be "included" in this place. Optionally, the :term:`application
namespace` and :term:`instance namespace` where the entries will be included
into can also be specified.
Usually, the application namespace should be specified by the included
module. If an application namespace is set, the ``namespace`` argument
can be used to set a different instance namespace.
``include()`` also accepts as an argument either an iterable that returns
URL patterns or a 2-tuple containing such iterable plus the names of the
application namespaces.
:arg module: URLconf module (or module name)
:arg namespace: Instance namespace for the URL entries being included
:type namespace: string
:arg pattern_list: Iterable of :func:`django.conf.urls.url` instances
:arg app_namespace: Application namespace for the URL entries being included
:type app_namespace: string
:arg instance_namespace: Instance namespace for the URL entries being included
:type instance_namespace: string
See :ref:`including-other-urlconfs` and :ref:`namespaces-and-include`.
.. versionchanged:: 2.0
In older versions, this function is located in ``django.conf.urls``. The
old location still works for backwards compatibility.
==================================================
``django.conf.urls`` functions for use in URLconfs
==================================================
.. module:: django.conf.urls
@ -50,37 +95,6 @@ function or method. See :ref:`views-extra-options` for an example.
See :ref:`Naming URL patterns <naming-url-patterns>` for why the ``name``
parameter is useful.
``include()``
=============
.. function:: include(module, namespace=None)
include(pattern_list)
include((pattern_list, app_namespace), namespace=None)
A function that takes a full Python import path to another URLconf module
that should be "included" in this place. Optionally, the :term:`application
namespace` and :term:`instance namespace` where the entries will be included
into can also be specified.
Usually, the application namespace should be specified by the included
module. If an application namespace is set, the ``namespace`` argument
can be used to set a different instance namespace.
``include()`` also accepts as an argument either an iterable that returns
URL patterns or a 2-tuple containing such iterable plus the names of the
application namespaces.
:arg module: URLconf module (or module name)
:arg namespace: Instance namespace for the URL entries being included
:type namespace: string
:arg pattern_list: Iterable of :func:`django.conf.urls.url` instances
:arg app_namespace: Application namespace for the URL entries being included
:type app_namespace: string
:arg instance_namespace: Instance namespace for the URL entries being included
:type instance_namespace: string
See :ref:`including-other-urlconfs` and :ref:`namespaces-and-include`.
``handler400``
==============

View File

@ -1158,19 +1158,19 @@ class IncludeTests(SimpleTestCase):
def test_include_namespace(self):
msg = (
"Specifying a namespace in django.conf.urls.include() without "
"providing an app_name is not supported."
'Specifying a namespace in include() without providing an '
'app_name is not supported.'
)
with self.assertRaisesMessage(ImproperlyConfigured, msg):
include(self.url_patterns, 'namespace')
def test_include_4_tuple(self):
msg = 'Passing a 4-tuple to django.conf.urls.include() is not supported.'
msg = 'Passing a 4-tuple to include() is not supported.'
with self.assertRaisesMessage(ImproperlyConfigured, msg):
include((self.url_patterns, 'app_name', 'namespace', 'blah'))
def test_include_3_tuple(self):
msg = 'Passing a 3-tuple to django.conf.urls.include() is not supported.'
msg = 'Passing a 3-tuple to include() is not supported.'
with self.assertRaisesMessage(ImproperlyConfigured, msg):
include((self.url_patterns, 'app_name', 'namespace'))