Refs #21927 -- Removed include()'s app_name argument per deprecation timeline.

Also removed support for passing a 3-tuple to include() and support for
setting an instance namespace without an application namespace.

Thanks Marten Kenbeek for completing the patch.
This commit is contained in:
Tim Graham 2017-01-10 09:57:49 -05:00
parent c6de8cca20
commit ad393beeb7
7 changed files with 119 additions and 127 deletions

View File

@ -1,4 +1,3 @@
import warnings
from importlib import import_module from importlib import import_module
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
@ -6,7 +5,6 @@ from django.urls import (
LocaleRegexURLResolver, RegexURLPattern, RegexURLResolver, LocaleRegexURLResolver, RegexURLPattern, RegexURLResolver,
) )
from django.utils import six from django.utils import six
from django.utils.deprecation import RemovedInDjango20Warning
__all__ = ['handler400', 'handler403', 'handler404', 'handler500', 'include', 'url'] __all__ = ['handler400', 'handler403', 'handler404', 'handler500', 'include', 'url']
@ -16,16 +14,8 @@ handler404 = 'django.views.defaults.page_not_found'
handler500 = 'django.views.defaults.server_error' handler500 = 'django.views.defaults.server_error'
def include(arg, namespace=None, app_name=None): def include(arg, namespace=None):
if app_name and not namespace: app_name = None
raise ValueError('Must specify a namespace if specifying app_name.')
if app_name:
warnings.warn(
'The app_name argument to django.conf.urls.include() is deprecated. '
'Set the app_name in the included URLconf instead.',
RemovedInDjango20Warning, stacklevel=2
)
if isinstance(arg, tuple): if isinstance(arg, tuple):
# callable returning a namespace hint # callable returning a namespace hint
try: try:
@ -35,13 +25,11 @@ def include(arg, namespace=None, app_name=None):
raise ImproperlyConfigured( raise ImproperlyConfigured(
'Cannot override the namespace for a dynamic module that provides a namespace' 'Cannot override the namespace for a dynamic module that provides a namespace'
) )
warnings.warn( raise ImproperlyConfigured(
'Passing a 3-tuple to django.conf.urls.include() is deprecated. ' 'Passing a 3-tuple to django.conf.urls.include() is not supported. '
'Pass a 2-tuple containing the list of patterns and app_name, ' 'Pass a 2-tuple containing the list of patterns and app_name, '
'and provide the namespace argument to include() instead.', 'and provide the namespace argument to include() instead.',
RemovedInDjango20Warning, stacklevel=2
) )
urlconf_module, app_name, namespace = arg
else: else:
# No namespace hint - use manually provided namespace # No namespace hint - use manually provided namespace
urlconf_module = arg urlconf_module = arg
@ -51,12 +39,11 @@ def include(arg, namespace=None, app_name=None):
patterns = getattr(urlconf_module, 'urlpatterns', urlconf_module) patterns = getattr(urlconf_module, 'urlpatterns', urlconf_module)
app_name = getattr(urlconf_module, 'app_name', app_name) app_name = getattr(urlconf_module, 'app_name', app_name)
if namespace and not app_name: if namespace and not app_name:
warnings.warn( raise ImproperlyConfigured(
'Specifying a namespace in django.conf.urls.include() without ' 'Specifying a namespace in django.conf.urls.include() without '
'providing an app_name is deprecated. Set the app_name attribute ' '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 ' 'in the included module, or pass a 2-tuple containing the list of '
'patterns and app_name instead.', 'patterns and app_name instead.',
RemovedInDjango20Warning, stacklevel=2
) )
namespace = namespace or app_name namespace = namespace or app_name

View File

@ -53,10 +53,9 @@ parameter is useful.
``include()`` ``include()``
============= =============
.. function:: include(module, namespace=None, app_name=None) .. function:: include(module, namespace=None)
include(pattern_list) include(pattern_list)
include((pattern_list, app_namespace), namespace=None) include((pattern_list, app_namespace), namespace=None)
include((pattern_list, app_namespace, instance_namespace))
A function that takes a full Python import path to another URLconf module A function that takes a full Python import path to another URLconf module
that should be "included" in this place. Optionally, the :term:`application that should be "included" in this place. Optionally, the :term:`application
@ -68,15 +67,12 @@ parameter is useful.
can be used to set a different instance namespace. can be used to set a different instance namespace.
``include()`` also accepts as an argument either an iterable that returns ``include()`` also accepts as an argument either an iterable that returns
URL patterns, a 2-tuple containing such iterable plus the names of the URL patterns or a 2-tuple containing such iterable plus the names of the
application namespaces, or a 3-tuple containing the iterable and the names application namespaces.
of both the application and instance namespace.
:arg module: URLconf module (or module name) :arg module: URLconf module (or module name)
:arg namespace: Instance namespace for the URL entries being included :arg namespace: Instance namespace for the URL entries being included
:type namespace: string :type namespace: string
:arg app_name: Application namespace for the URL entries being included
:type app_name: string
:arg pattern_list: Iterable of :func:`django.conf.urls.url` instances :arg pattern_list: Iterable of :func:`django.conf.urls.url` instances
:arg app_namespace: Application namespace for the URL entries being included :arg app_namespace: Application namespace for the URL entries being included
:type app_namespace: string :type app_namespace: string
@ -85,20 +81,6 @@ parameter is useful.
See :ref:`including-other-urlconfs` and :ref:`namespaces-and-include`. See :ref:`including-other-urlconfs` and :ref:`namespaces-and-include`.
.. deprecated:: 1.9
Support for the ``app_name`` argument is deprecated and will be removed in
Django 2.0. Specify the ``app_name`` as explained in
:ref:`namespaces-and-include` instead.
Support for passing a 3-tuple is also deprecated and will be removed in
Django 2.0. Pass a 2-tuple containing the pattern list and application
namespace, and use the ``namespace`` argument instead.
Lastly, support for an instance namespace without an application namespace
has been deprecated and will be removed in Django 2.0. Specify the
application namespace or remove the instance namespace.
``handler400`` ``handler400``
============== ==============

View File

@ -291,3 +291,11 @@ these features.
* The ``mime_type`` attribute of ``django.utils.feedgenerator.Atom1Feed`` and * The ``mime_type`` attribute of ``django.utils.feedgenerator.Atom1Feed`` and
``django.utils.feedgenerator.RssFeed`` is removed. ``django.utils.feedgenerator.RssFeed`` is removed.
* The ``app_name`` argument to ``include()`` is removed.
* Support for passing a 3-tuple as the first argument to ``include()`` is
removed.
* Support for setting a URL instance namespace without an application namespace
is removed.

View File

@ -6,6 +6,7 @@ from .views import empty_view, view_class_instance
testobj3 = URLObject('testapp', 'test-ns3') testobj3 = URLObject('testapp', 'test-ns3')
testobj4 = URLObject('testapp', 'test-ns4') testobj4 = URLObject('testapp', 'test-ns4')
app_name = 'included_namespace_urls'
urlpatterns = [ urlpatterns = [
url(r'^normal/$', empty_view, name='inc-normal-view'), url(r'^normal/$', empty_view, name='inc-normal-view'),
url(r'^normal/(?P<arg1>[0-9]+)/(?P<arg2>[0-9]+)/$', empty_view, name='inc-normal-view'), url(r'^normal/(?P<arg1>[0-9]+)/(?P<arg2>[0-9]+)/$', empty_view, name='inc-normal-view'),
@ -17,8 +18,8 @@ urlpatterns = [
url(r'^view_class/(?P<arg1>[0-9]+)/(?P<arg2>[0-9]+)/$', view_class_instance, name='inc-view-class'), url(r'^view_class/(?P<arg1>[0-9]+)/(?P<arg2>[0-9]+)/$', view_class_instance, name='inc-view-class'),
url(r'^test3/', include(testobj3.urls)), url(r'^test3/', include(*testobj3.urls)),
url(r'^test4/', include(testobj4.urls)), url(r'^test4/', include(*testobj4.urls)),
url(r'^ns-included3/', include('urlpatterns_reverse.included_urls', namespace='inc-ns3')), url(r'^ns-included3/', include(('urlpatterns_reverse.included_urls', 'included_urls'), namespace='inc-ns3')),
url(r'^ns-included4/', include('urlpatterns_reverse.namespace_urls', namespace='inc-ns4')), url(r'^ns-included4/', include('urlpatterns_reverse.namespace_urls', namespace='inc-ns4')),
] ]

View File

@ -12,6 +12,7 @@ otherobj2 = URLObject('nodefault', 'other-ns2')
newappobj1 = URLObject('newapp') newappobj1 = URLObject('newapp')
app_name = 'namespace_urls'
urlpatterns = [ urlpatterns = [
url(r'^normal/$', views.empty_view, name='normal-view'), url(r'^normal/$', views.empty_view, name='normal-view'),
url(r'^normal/(?P<arg1>[0-9]+)/(?P<arg2>[0-9]+)/$', views.empty_view, name='normal-view'), url(r'^normal/(?P<arg1>[0-9]+)/(?P<arg2>[0-9]+)/$', views.empty_view, name='normal-view'),
@ -27,12 +28,12 @@ urlpatterns = [
url(r'^unnamed/normal/(?P<arg1>[0-9]+)/(?P<arg2>[0-9]+)/$', views.empty_view), url(r'^unnamed/normal/(?P<arg1>[0-9]+)/(?P<arg2>[0-9]+)/$', views.empty_view),
url(r'^unnamed/view_class/(?P<arg1>[0-9]+)/(?P<arg2>[0-9]+)/$', views.view_class_instance), url(r'^unnamed/view_class/(?P<arg1>[0-9]+)/(?P<arg2>[0-9]+)/$', views.view_class_instance),
url(r'^test1/', include(testobj1.urls)), url(r'^test1/', include(*testobj1.urls)),
url(r'^test2/', include(testobj2.urls)), url(r'^test2/', include(*testobj2.urls)),
url(r'^default/', include(default_testobj.urls)), url(r'^default/', include(*default_testobj.urls)),
url(r'^other1/', include(otherobj1.urls)), url(r'^other1/', include(*otherobj1.urls)),
url(r'^other[246]/', include(otherobj2.urls)), url(r'^other[246]/', include(*otherobj2.urls)),
url(r'^newapp1/', include(newappobj1.app_urls, 'new-ns1')), url(r'^newapp1/', include(newappobj1.app_urls, 'new-ns1')),
url(r'^new-default/', include(newappobj1.app_urls)), url(r'^new-default/', include(newappobj1.app_urls)),
@ -43,10 +44,12 @@ urlpatterns = [
url(r'^ns-included[135]/', include('urlpatterns_reverse.included_namespace_urls', namespace='inc-ns1')), url(r'^ns-included[135]/', include('urlpatterns_reverse.included_namespace_urls', namespace='inc-ns1')),
url(r'^ns-included2/', include('urlpatterns_reverse.included_namespace_urls', namespace='inc-ns2')), url(r'^ns-included2/', include('urlpatterns_reverse.included_namespace_urls', namespace='inc-ns2')),
url(r'^app-included/', include('urlpatterns_reverse.included_namespace_urls', 'inc-app', 'inc-app')), url(r'^app-included/', include('urlpatterns_reverse.included_namespace_urls', 'inc-app')),
url(r'^included/', include('urlpatterns_reverse.included_namespace_urls')), url(r'^included/', include('urlpatterns_reverse.included_namespace_urls')),
url(r'^inc(?P<outer>[0-9]+)/', include('urlpatterns_reverse.included_urls', namespace='inc-ns5')), url(
r'^inc(?P<outer>[0-9]+)/', include(('urlpatterns_reverse.included_urls', 'included_urls'), namespace='inc-ns5')
),
url(r'^included/([0-9]+)/', include('urlpatterns_reverse.included_namespace_urls')), url(r'^included/([0-9]+)/', include('urlpatterns_reverse.included_namespace_urls')),
url( url(

View File

@ -18,16 +18,13 @@ from django.http import (
HttpRequest, HttpResponsePermanentRedirect, HttpResponseRedirect, HttpRequest, HttpResponsePermanentRedirect, HttpResponseRedirect,
) )
from django.shortcuts import redirect from django.shortcuts import redirect
from django.test import ( from django.test import SimpleTestCase, TestCase, override_settings
SimpleTestCase, TestCase, ignore_warnings, override_settings,
)
from django.test.utils import override_script_prefix from django.test.utils import override_script_prefix
from django.urls import ( from django.urls import (
NoReverseMatch, RegexURLPattern, RegexURLResolver, Resolver404, NoReverseMatch, RegexURLPattern, RegexURLResolver, Resolver404,
ResolverMatch, get_callable, get_resolver, resolve, reverse, reverse_lazy, ResolverMatch, get_callable, get_resolver, resolve, reverse, reverse_lazy,
) )
from django.utils import six from django.utils import six
from django.utils.deprecation import RemovedInDjango20Warning
from . import middleware, urlconf_outer, views from . import middleware, urlconf_outer, views
from .utils import URLObject from .utils import URLObject
@ -42,23 +39,27 @@ resolve_test_data = (
{'arg1': '42', 'arg2': '37'} {'arg1': '42', 'arg2': '37'}
), ),
( (
'/included/normal/42/37/', 'inc-normal-view', '', '', 'inc-normal-view', views.empty_view, tuple(), '/included/normal/42/37/', 'inc-normal-view', 'included_namespace_urls',
{'arg1': '42', 'arg2': '37'} 'included_namespace_urls', 'included_namespace_urls:inc-normal-view',
views.empty_view, tuple(), {'arg1': '42', 'arg2': '37'}
), ),
( (
'/included/view_class/42/37/', 'inc-view-class', '', '', 'inc-view-class', views.view_class_instance, tuple(), '/included/view_class/42/37/', 'inc-view-class', 'included_namespace_urls',
{'arg1': '42', 'arg2': '37'} 'included_namespace_urls', 'included_namespace_urls:inc-view-class',
views.view_class_instance, tuple(), {'arg1': '42', 'arg2': '37'}
), ),
# Unnamed args are dropped if you have *any* kwargs in a pattern # Unnamed args are dropped if you have *any* kwargs in a pattern
('/mixed_args/42/37/', 'mixed-args', '', '', 'mixed-args', views.empty_view, tuple(), {'arg2': '37'}), ('/mixed_args/42/37/', 'mixed-args', '', '', 'mixed-args', views.empty_view, tuple(), {'arg2': '37'}),
( (
'/included/mixed_args/42/37/', 'inc-mixed-args', '', '', 'inc-mixed-args', views.empty_view, tuple(), '/included/mixed_args/42/37/', 'inc-mixed-args', 'included_namespace_urls',
{'arg2': '37'} 'included_namespace_urls', 'included_namespace_urls:inc-mixed-args',
views.empty_view, tuple(), {'arg2': '37'}
), ),
( (
'/included/12/mixed_args/42/37/', 'inc-mixed-args', '', '', 'inc-mixed-args', views.empty_view, tuple(), '/included/12/mixed_args/42/37/', 'inc-mixed-args', 'included_namespace_urls',
{'arg2': '37'} 'included_namespace_urls', 'included_namespace_urls:inc-mixed-args',
views.empty_view, tuple(), {'arg2': '37'}
), ),
# Unnamed views should have None as the url_name. Regression data for #21157. # Unnamed views should have None as the url_name. Regression data for #21157.
@ -73,10 +74,15 @@ resolve_test_data = (
# If you have no kwargs, you get an args list. # If you have no kwargs, you get an args list.
('/no_kwargs/42/37/', 'no-kwargs', '', '', 'no-kwargs', views.empty_view, ('42', '37'), {}), ('/no_kwargs/42/37/', 'no-kwargs', '', '', 'no-kwargs', views.empty_view, ('42', '37'), {}),
('/included/no_kwargs/42/37/', 'inc-no-kwargs', '', '', 'inc-no-kwargs', views.empty_view, ('42', '37'), {}),
( (
'/included/12/no_kwargs/42/37/', 'inc-no-kwargs', '', '', 'inc-no-kwargs', views.empty_view, '/included/no_kwargs/42/37/', 'inc-no-kwargs', 'included_namespace_urls',
('12', '42', '37'), {} 'included_namespace_urls', 'included_namespace_urls:inc-no-kwargs',
views.empty_view, ('42', '37'), {}
),
(
'/included/12/no_kwargs/42/37/', 'inc-no-kwargs', 'included_namespace_urls',
'included_namespace_urls', 'included_namespace_urls:inc-no-kwargs',
views.empty_view, ('12', '42', '37'), {}
), ),
# Namespaces # Namespaces
@ -85,20 +91,23 @@ resolve_test_data = (
views.empty_view, tuple(), {'arg1': '42', 'arg2': '37'} views.empty_view, tuple(), {'arg1': '42', 'arg2': '37'}
), ),
( (
'/included/test3/inner/42/37/', 'urlobject-view', 'testapp', 'test-ns3', 'test-ns3:urlobject-view', '/included/test3/inner/42/37/', 'urlobject-view', 'included_namespace_urls:testapp',
'included_namespace_urls:test-ns3', 'included_namespace_urls:test-ns3:urlobject-view',
views.empty_view, tuple(), {'arg1': '42', 'arg2': '37'} views.empty_view, tuple(), {'arg1': '42', 'arg2': '37'}
), ),
( (
'/ns-included1/normal/42/37/', 'inc-normal-view', '', 'inc-ns1', 'inc-ns1:inc-normal-view', views.empty_view, '/ns-included1/normal/42/37/', 'inc-normal-view', 'included_namespace_urls',
tuple(), {'arg1': '42', 'arg2': '37'} 'inc-ns1', 'inc-ns1:inc-normal-view',
),
(
'/included/test3/inner/42/37/', 'urlobject-view', 'testapp', 'test-ns3', 'test-ns3:urlobject-view',
views.empty_view, tuple(), {'arg1': '42', 'arg2': '37'} views.empty_view, tuple(), {'arg1': '42', 'arg2': '37'}
), ),
( (
'/default/inner/42/37/', 'urlobject-view', 'testapp', 'testapp', 'testapp:urlobject-view', views.empty_view, '/included/test3/inner/42/37/', 'urlobject-view', 'included_namespace_urls:testapp',
tuple(), {'arg1': '42', 'arg2': '37'} 'included_namespace_urls:test-ns3', 'included_namespace_urls:test-ns3:urlobject-view',
views.empty_view, tuple(), {'arg1': '42', 'arg2': '37'}
),
(
'/default/inner/42/37/', 'urlobject-view', 'testapp', 'testapp', 'testapp:urlobject-view',
views.empty_view, tuple(), {'arg1': '42', 'arg2': '37'}
), ),
( (
'/other2/inner/42/37/', 'urlobject-view', 'nodefault', 'other-ns2', 'other-ns2:urlobject-view', '/other2/inner/42/37/', 'urlobject-view', 'nodefault', 'other-ns2', 'other-ns2:urlobject-view',
@ -111,29 +120,37 @@ resolve_test_data = (
# Nested namespaces # Nested namespaces
( (
'/ns-included1/test3/inner/42/37/', 'urlobject-view', 'testapp', 'inc-ns1:test-ns3', '/ns-included1/test3/inner/42/37/', 'urlobject-view', 'included_namespace_urls:testapp',
'inc-ns1:test-ns3:urlobject-view', views.empty_view, tuple(), {'arg1': '42', 'arg2': '37'} 'inc-ns1:test-ns3', 'inc-ns1:test-ns3:urlobject-view',
views.empty_view, tuple(), {'arg1': '42', 'arg2': '37'}
), ),
( (
'/ns-included1/ns-included4/ns-included2/test3/inner/42/37/', 'urlobject-view', 'testapp', '/ns-included1/ns-included4/ns-included2/test3/inner/42/37/', 'urlobject-view',
'inc-ns1:inc-ns4:inc-ns2:test-ns3', 'inc-ns1:inc-ns4:inc-ns2:test-ns3:urlobject-view', views.empty_view, 'included_namespace_urls:namespace_urls:included_namespace_urls:testapp',
tuple(), {'arg1': '42', 'arg2': '37'} 'inc-ns1:inc-ns4:inc-ns2:test-ns3',
'inc-ns1:inc-ns4:inc-ns2:test-ns3:urlobject-view',
views.empty_view, tuple(), {'arg1': '42', 'arg2': '37'}
), ),
( (
'/app-included/test3/inner/42/37/', 'urlobject-view', 'inc-app:testapp', 'inc-app:test-ns3', '/app-included/test3/inner/42/37/', 'urlobject-view', 'included_namespace_urls:testapp', 'inc-app:test-ns3',
'inc-app:test-ns3:urlobject-view', views.empty_view, tuple(), {'arg1': '42', 'arg2': '37'} 'inc-app:test-ns3:urlobject-view', views.empty_view, tuple(), {'arg1': '42', 'arg2': '37'}
), ),
( (
'/app-included/ns-included4/ns-included2/test3/inner/42/37/', 'urlobject-view', 'inc-app:testapp', '/app-included/ns-included4/ns-included2/test3/inner/42/37/', 'urlobject-view',
'inc-app:inc-ns4:inc-ns2:test-ns3', 'inc-app:inc-ns4:inc-ns2:test-ns3:urlobject-view', views.empty_view, 'included_namespace_urls:namespace_urls:included_namespace_urls:testapp',
tuple(), {'arg1': '42', 'arg2': '37'} 'inc-app:inc-ns4:inc-ns2:test-ns3',
'inc-app:inc-ns4:inc-ns2:test-ns3:urlobject-view',
views.empty_view, tuple(), {'arg1': '42', 'arg2': '37'}
), ),
# Namespaces capturing variables # Namespaces capturing variables
('/inc70/', 'inner-nothing', '', 'inc-ns5', 'inc-ns5:inner-nothing', views.empty_view, tuple(), {'outer': '70'}),
( (
'/inc78/extra/foobar/', 'inner-extra', '', 'inc-ns5', 'inc-ns5:inner-extra', views.empty_view, tuple(), '/inc70/', 'inner-nothing', 'included_urls', 'inc-ns5', 'inc-ns5:inner-nothing',
{'outer': '78', 'extra': 'foobar'} views.empty_view, tuple(), {'outer': '70'}
),
(
'/inc78/extra/foobar/', 'inner-extra', 'included_urls', 'inc-ns5', 'inc-ns5:inner-extra',
views.empty_view, tuple(), {'outer': '78', 'extra': 'foobar'}
), ),
) )
@ -355,7 +372,6 @@ class URLPatternReverse(SimpleTestCase):
class ResolverTests(SimpleTestCase): class ResolverTests(SimpleTestCase):
@ignore_warnings(category=RemovedInDjango20Warning)
def test_resolver_repr(self): def test_resolver_repr(self):
""" """
Test repr of RegexURLResolver, especially when urlconf_name is a list Test repr of RegexURLResolver, especially when urlconf_name is a list
@ -576,7 +592,6 @@ class ReverseShortcutTests(SimpleTestCase):
@override_settings(ROOT_URLCONF='urlpatterns_reverse.namespace_urls') @override_settings(ROOT_URLCONF='urlpatterns_reverse.namespace_urls')
@ignore_warnings(category=RemovedInDjango20Warning)
class NamespaceTests(SimpleTestCase): class NamespaceTests(SimpleTestCase):
def test_ambiguous_object(self): def test_ambiguous_object(self):
@ -613,10 +628,13 @@ class NamespaceTests(SimpleTestCase):
def test_simple_included_name(self): def test_simple_included_name(self):
"Normal lookups work on names included from other patterns" "Normal lookups work on names included from other patterns"
self.assertEqual('/included/normal/', reverse('inc-normal-view')) self.assertEqual('/included/normal/', reverse('included_namespace_urls:inc-normal-view'))
self.assertEqual('/included/normal/37/42/', reverse('inc-normal-view', args=[37, 42])) self.assertEqual('/included/normal/37/42/', reverse('included_namespace_urls:inc-normal-view', args=[37, 42]))
self.assertEqual('/included/normal/42/37/', reverse('inc-normal-view', kwargs={'arg1': 42, 'arg2': 37})) self.assertEqual(
self.assertEqual('/included/+%5C$*/', reverse('inc-special-view')) '/included/normal/42/37/',
reverse('included_namespace_urls:inc-normal-view', kwargs={'arg1': 42, 'arg2': 37})
)
self.assertEqual('/included/+%5C$*/', reverse('included_namespace_urls:inc-special-view'))
def test_namespace_object(self): def test_namespace_object(self):
"Dynamic URL objects can be found using a namespace" "Dynamic URL objects can be found using a namespace"
@ -643,12 +661,17 @@ class NamespaceTests(SimpleTestCase):
def test_embedded_namespace_object(self): def test_embedded_namespace_object(self):
"Namespaces can be installed anywhere in the URL pattern tree" "Namespaces can be installed anywhere in the URL pattern tree"
self.assertEqual('/included/test3/inner/', reverse('test-ns3:urlobject-view')) self.assertEqual('/included/test3/inner/', reverse('included_namespace_urls:test-ns3:urlobject-view'))
self.assertEqual('/included/test3/inner/37/42/', reverse('test-ns3:urlobject-view', args=[37, 42]))
self.assertEqual( self.assertEqual(
'/included/test3/inner/42/37/', reverse('test-ns3:urlobject-view', kwargs={'arg1': 42, 'arg2': 37}) '/included/test3/inner/37/42/', reverse('included_namespace_urls:test-ns3:urlobject-view', args=[37, 42])
)
self.assertEqual(
'/included/test3/inner/42/37/',
reverse('included_namespace_urls:test-ns3:urlobject-view', kwargs={'arg1': 42, 'arg2': 37})
)
self.assertEqual(
'/included/test3/inner/+%5C$*/', reverse('included_namespace_urls:test-ns3:urlobject-special-view')
) )
self.assertEqual('/included/test3/inner/+%5C$*/', reverse('test-ns3:urlobject-special-view'))
def test_namespace_pattern(self): def test_namespace_pattern(self):
"Namespaces can be applied to include()'d urlpatterns" "Namespaces can be applied to include()'d urlpatterns"
@ -718,17 +741,17 @@ class NamespaceTests(SimpleTestCase):
def test_app_lookup_object_with_default(self): def test_app_lookup_object_with_default(self):
"A default application namespace is sensitive to the 'current' app can be used for lookup" "A default application namespace is sensitive to the 'current' app can be used for lookup"
self.assertEqual('/included/test3/inner/', reverse('testapp:urlobject-view', current_app='test-ns3')) self.assertEqual('/default/inner/', reverse('testapp:urlobject-view', current_app='test-ns3'))
self.assertEqual( self.assertEqual(
'/included/test3/inner/37/42/', '/default/inner/37/42/',
reverse('testapp:urlobject-view', args=[37, 42], current_app='test-ns3') reverse('testapp:urlobject-view', args=[37, 42], current_app='test-ns3')
) )
self.assertEqual( self.assertEqual(
'/included/test3/inner/42/37/', '/default/inner/42/37/',
reverse('testapp:urlobject-view', kwargs={'arg1': 42, 'arg2': 37}, current_app='test-ns3') reverse('testapp:urlobject-view', kwargs={'arg1': 42, 'arg2': 37}, current_app='test-ns3')
) )
self.assertEqual( self.assertEqual(
'/included/test3/inner/+%5C$*/', reverse('testapp:urlobject-special-view', current_app='test-ns3') '/default/inner/+%5C$*/', reverse('testapp:urlobject-special-view', current_app='test-ns3')
) )
def test_app_lookup_object_without_default(self): def test_app_lookup_object_without_default(self):
@ -749,13 +772,16 @@ class NamespaceTests(SimpleTestCase):
self.assertEqual('/other1/inner/+%5C$*/', reverse('nodefault:urlobject-special-view', current_app='other-ns1')) self.assertEqual('/other1/inner/+%5C$*/', reverse('nodefault:urlobject-special-view', current_app='other-ns1'))
def test_special_chars_namespace(self): def test_special_chars_namespace(self):
self.assertEqual('/+%5C$*/included/normal/', reverse('special:inc-normal-view')) self.assertEqual('/+%5C$*/included/normal/', reverse('special:included_namespace_urls:inc-normal-view'))
self.assertEqual('/+%5C$*/included/normal/37/42/', reverse('special:inc-normal-view', args=[37, 42])) self.assertEqual(
'/+%5C$*/included/normal/37/42/',
reverse('special:included_namespace_urls:inc-normal-view', args=[37, 42])
)
self.assertEqual( self.assertEqual(
'/+%5C$*/included/normal/42/37/', '/+%5C$*/included/normal/42/37/',
reverse('special:inc-normal-view', kwargs={'arg1': 42, 'arg2': 37}) reverse('special:included_namespace_urls:inc-normal-view', kwargs={'arg1': 42, 'arg2': 37})
) )
self.assertEqual('/+%5C$*/included/+%5C$*/', reverse('special:inc-special-view')) self.assertEqual('/+%5C$*/included/+%5C$*/', reverse('special:included_namespace_urls:inc-special-view'))
def test_namespaces_with_variables(self): def test_namespaces_with_variables(self):
"Namespace prefixes can capture variables: see #15900" "Namespace prefixes can capture variables: see #15900"
@ -965,7 +991,6 @@ class NoRootUrlConfTests(SimpleTestCase):
@override_settings(ROOT_URLCONF='urlpatterns_reverse.namespace_urls') @override_settings(ROOT_URLCONF='urlpatterns_reverse.namespace_urls')
class ResolverMatchTests(SimpleTestCase): class ResolverMatchTests(SimpleTestCase):
@ignore_warnings(category=RemovedInDjango20Warning)
def test_urlpattern_resolve(self): def test_urlpattern_resolve(self):
for path, url_name, app_name, namespace, view_name, func, args, kwargs in resolve_test_data: for path, url_name, app_name, namespace, view_name, func, args, kwargs in resolve_test_data:
# Test legacy support for extracting "function, args, kwargs" # Test legacy support for extracting "function, args, kwargs"
@ -990,7 +1015,6 @@ class ResolverMatchTests(SimpleTestCase):
self.assertEqual(match[1], args) self.assertEqual(match[1], args)
self.assertEqual(match[2], kwargs) self.assertEqual(match[2], kwargs)
@ignore_warnings(category=RemovedInDjango20Warning)
def test_resolver_match_on_request(self): def test_resolver_match_on_request(self):
response = self.client.get('/resolver_match/') response = self.client.get('/resolver_match/')
resolver_match = response.resolver_match resolver_match = response.resolver_match
@ -1042,34 +1066,21 @@ class IncludeTests(SimpleTestCase):
] ]
app_urls = URLObject('inc-app') app_urls = URLObject('inc-app')
def test_include_app_name_but_no_namespace(self):
msg = "Must specify a namespace if specifying app_name."
with self.assertRaisesMessage(ValueError, msg):
include(self.url_patterns, app_name='bar')
def test_include_urls(self): def test_include_urls(self):
self.assertEqual(include(self.url_patterns), (self.url_patterns, None, None)) self.assertEqual(include(self.url_patterns), (self.url_patterns, None, None))
@ignore_warnings(category=RemovedInDjango20Warning)
def test_include_namespace(self): def test_include_namespace(self):
# no app_name -> deprecated msg = (
self.assertEqual(include(self.url_patterns, 'namespace'), (self.url_patterns, None, 'namespace')) "Specifying a namespace in django.conf.urls.include() without "
"providing an app_name is not supported."
@ignore_warnings(category=RemovedInDjango20Warning)
def test_include_namespace_app_name(self):
# app_name argument to include -> deprecated
self.assertEqual(
include(self.url_patterns, 'namespace', 'app_name'),
(self.url_patterns, 'app_name', 'namespace')
) )
with self.assertRaisesMessage(ImproperlyConfigured, msg):
include(self.url_patterns, 'namespace')
@ignore_warnings(category=RemovedInDjango20Warning)
def test_include_3_tuple(self): def test_include_3_tuple(self):
# 3-tuple -> deprecated msg = 'Passing a 3-tuple to django.conf.urls.include() is not supported.'
self.assertEqual( with self.assertRaisesMessage(ImproperlyConfigured, msg):
include((self.url_patterns, 'app_name', 'namespace')), include((self.url_patterns, 'app_name', 'namespace'))
(self.url_patterns, 'app_name', 'namespace')
)
def test_include_2_tuple(self): def test_include_2_tuple(self):
self.assertEqual( self.assertEqual(

View File

@ -18,7 +18,7 @@ class URLObject(object):
@property @property
def urls(self): def urls(self):
return self.urlpatterns, self.app_name, self.namespace return (self.urlpatterns, self.app_name), self.namespace
@property @property
def app_urls(self): def app_urls(self):