From ee4043f7355f077583ca4c1749db7d5211f0855e Mon Sep 17 00:00:00 2001 From: Tim Graham Date: Wed, 13 Sep 2017 15:24:12 -0400 Subject: [PATCH] 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. --- django/conf/urls/__init__.py | 55 +------------------- django/urls/__init__.py | 5 +- django/urls/conf.py | 50 ++++++++++++++++++ docs/ref/urls.txt | 82 +++++++++++++++++------------- tests/urlpatterns_reverse/tests.py | 8 +-- 5 files changed, 106 insertions(+), 94 deletions(-) create mode 100644 django/urls/conf.py diff --git a/django/conf/urls/__init__.py b/django/conf/urls/__init__.py index bfb0961cfc0..b7ad1561228 100644 --- a/django/conf/urls/__init__.py +++ b/django/conf/urls/__init__.py @@ -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. diff --git a/django/urls/__init__.py b/django/urls/__init__.py index 21b6da0a624..f6d51e8a9f7 100644 --- a/django/urls/__init__.py +++ b/django/urls/__init__.py @@ -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', ] diff --git a/django/urls/conf.py b/django/urls/conf.py new file mode 100644 index 00000000000..99bff55819c --- /dev/null +++ b/django/urls/conf.py @@ -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) diff --git a/docs/ref/urls.txt b/docs/ref/urls.txt index d44cce80da9..3b6a66032d6 100644 --- a/docs/ref/urls.txt +++ b/docs/ref/urls.txt @@ -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 ` 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`` ============== diff --git a/tests/urlpatterns_reverse/tests.py b/tests/urlpatterns_reverse/tests.py index 7f565fe4870..91ad5a867fc 100644 --- a/tests/urlpatterns_reverse/tests.py +++ b/tests/urlpatterns_reverse/tests.py @@ -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'))