summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--django/conf/project_template/project_name/urls.py4
-rw-r--r--django/conf/urls/__init__.py36
-rw-r--r--docs/internals/deprecation.txt9
-rw-r--r--docs/intro/tutorial01.txt10
-rw-r--r--docs/intro/tutorial03.txt16
-rw-r--r--docs/ref/contrib/admin/index.txt22
-rw-r--r--docs/ref/contrib/gis/tutorial.txt2
-rw-r--r--docs/ref/urls.txt24
-rw-r--r--docs/releases/1.9.txt39
-rw-r--r--docs/topics/http/urls.txt78
-rw-r--r--docs/topics/i18n/translation.txt8
-rw-r--r--tests/admin_changelist/urls.py4
-rw-r--r--tests/admin_custom_urls/urls.py4
-rw-r--r--tests/admin_docs/urls.py6
-rw-r--r--tests/admin_inlines/urls.py4
-rw-r--r--tests/admin_views/test_adminsite.py4
-rw-r--r--tests/admin_views/urls.py14
-rw-r--r--tests/admin_widgets/urls.py4
-rw-r--r--tests/auth_tests/urls.py4
-rw-r--r--tests/auth_tests/urls_admin.py4
-rw-r--r--tests/auth_tests/urls_custom_user_admin.py4
-rw-r--r--tests/generic_inline_admin/urls.py4
-rw-r--r--tests/i18n/patterns/urls/namespace.py1
-rw-r--r--tests/i18n/patterns/urls/wrong_namespace.py1
-rw-r--r--tests/proxy_models/urls.py4
-rw-r--r--tests/timezones/urls.py4
-rw-r--r--tests/urlpatterns_reverse/included_app_urls.py16
-rw-r--r--tests/urlpatterns_reverse/namespace_urls.py23
-rw-r--r--tests/urlpatterns_reverse/tests.py108
-rw-r--r--tests/view_tests/default_urls.py4
30 files changed, 352 insertions, 113 deletions
diff --git a/django/conf/project_template/project_name/urls.py b/django/conf/project_template/project_name/urls.py
index 665085b17b..c0cfc4e2a3 100644
--- a/django/conf/project_template/project_name/urls.py
+++ b/django/conf/project_template/project_name/urls.py
@@ -13,9 +13,9 @@ Including another URLconf
1. Add an import: from blog import urls as blog_urls
2. Add a URL to urlpatterns: url(r'^blog/', include(blog_urls))
"""
-from django.conf.urls import include, url
+from django.conf.urls import url
from django.contrib import admin
urlpatterns = [
- url(r'^admin/', include(admin.site.urls)),
+ url(r'^admin/', admin.site.urls),
]
diff --git a/django/conf/urls/__init__.py b/django/conf/urls/__init__.py
index 6ed54930c4..64bec13ec1 100644
--- a/django/conf/urls/__init__.py
+++ b/django/conf/urls/__init__.py
@@ -5,7 +5,7 @@ from django.core.urlresolvers import (RegexURLPattern,
RegexURLResolver, LocaleRegexURLResolver)
from django.core.exceptions import ImproperlyConfigured
from django.utils import six
-from django.utils.deprecation import RemovedInDjango20Warning
+from django.utils.deprecation import RemovedInDjango20Warning, RemovedInDjango21Warning
__all__ = ['handler400', 'handler403', 'handler404', 'handler500', 'include', 'patterns', 'url']
@@ -19,12 +19,29 @@ handler500 = 'django.views.defaults.server_error'
def include(arg, namespace=None, app_name=None):
if app_name and not namespace:
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.',
+ RemovedInDjango21Warning, stacklevel=2
+ )
if isinstance(arg, tuple):
# callable returning a namespace hint
- if namespace:
- raise ImproperlyConfigured('Cannot override the namespace for a dynamic module that provides a namespace')
- urlconf_module, app_name, namespace = arg
+ try:
+ urlconf_module, app_name = arg
+ except ValueError:
+ if namespace:
+ raise ImproperlyConfigured(
+ 'Cannot override the namespace for a dynamic module that provides a namespace'
+ )
+ warnings.warn(
+ 'Passing a 3-tuple to django.conf.urls.include() is deprecated. '
+ 'Pass a 2-tuple containing the list of patterns and app_name, '
+ 'and provide the namespace argument to include() instead.',
+ RemovedInDjango21Warning, stacklevel=2
+ )
+ urlconf_module, app_name, namespace = arg
else:
# No namespace hint - use manually provided namespace
urlconf_module = arg
@@ -32,6 +49,17 @@ def include(arg, namespace=None, app_name=None):
if isinstance(urlconf_module, six.string_types):
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:
+ warnings.warn(
+ 'Specifying a namespace in django.conf.urls.include() without '
+ 'providing an app_name is deprecated. Set the app_name attribute '
+ 'in the included module, or pass a 2-tuple containing the list of '
+ 'patterns and app_name instead.',
+ RemovedInDjango21Warning, stacklevel=2
+ )
+
+ namespace = namespace or app_name
# Make sure we can iterate through the patterns (without this, some
# testcases will break).
diff --git a/docs/internals/deprecation.txt b/docs/internals/deprecation.txt
index bc55472121..1737681f88 100644
--- a/docs/internals/deprecation.txt
+++ b/docs/internals/deprecation.txt
@@ -70,6 +70,15 @@ details on these changes.
``django.utils.feedgenerator.RssFeed`` will be removed in favor of
``content_type``.
+* The ``app_name`` argument to :func:`~django.conf.urls.include()` will be
+ removed.
+
+* Support for passing a 3-tuple as the first argument to ``include()`` will
+ be removed.
+
+* Support for setting a URL instance namespace without an application
+ namespace will be removed.
+
.. _deprecation-removed-in-2.0:
2.0
diff --git a/docs/intro/tutorial01.txt b/docs/intro/tutorial01.txt
index 4eb8ecca4d..6db12d118b 100644
--- a/docs/intro/tutorial01.txt
+++ b/docs/intro/tutorial01.txt
@@ -293,15 +293,15 @@ with:
urlpatterns = [
url(r'^polls/', include('polls.urls')),
- url(r'^admin/', include(admin.site.urls)),
+ url(r'^admin/', admin.site.urls),
]
.. admonition:: Doesn't match what you see?
- If you're seeing ``admin.autodiscover()`` before the definition of
- ``urlpatterns``, you're probably using a version of Django that doesn't
- match this tutorial version. You'll want to either switch to the older
- tutorial or the newer Django version.
+ If you're seeing ``include(admin.site.urls)`` instead of just
+ ``admin.site.urls``, you're probably using a version of Django that
+ doesn't match this tutorial version. You'll want to either switch to the
+ older tutorial or the newer Django version.
You have now wired an ``index`` view into the URLconf. Lets verify it's
working, run the following command:
diff --git a/docs/intro/tutorial03.txt b/docs/intro/tutorial03.txt
index 5b6c3e5ce7..9da44c3caf 100644
--- a/docs/intro/tutorial03.txt
+++ b/docs/intro/tutorial03.txt
@@ -442,18 +442,20 @@ view, and so might an app on the same project that is for a blog. How does one
make it so that Django knows which app view to create for a url when using the
``{% url %}`` template tag?
-The answer is to add namespaces to your root URLconf. In the ``mysite/urls.py``
-file, go ahead and change it to include namespacing:
+The answer is to add namespaces to your URLconf. In the ``polls/urls.py``
+file, go ahead and add an ``app_name`` to set the application namespace:
.. snippet::
- :filename: mysite/urls.py
+ :filename: polls/urls.py
- from django.conf.urls import include, url
- from django.contrib import admin
+ from django.conf.urls import url
+ app_name = 'polls'
urlpatterns = [
- url(r'^polls/', include('polls.urls', namespace="polls")),
- url(r'^admin/', include(admin.site.urls)),
+ url(r'^$', views.index, name='index'),
+ url(r'^(?P<question_id>[0-9]+)/$', views.detail, name='detail'),
+ url(r'^(?P<question_id>[0-9]+)/results/$', views.results, name='results'),
+ url(r'^(?P<question_id>[0-9]+)/vote/$', views.vote, name='vote'),
]
Now change your ``polls/index.html`` template from:
diff --git a/docs/ref/contrib/admin/index.txt b/docs/ref/contrib/admin/index.txt
index 091967c796..f4480d6a3b 100644
--- a/docs/ref/contrib/admin/index.txt
+++ b/docs/ref/contrib/admin/index.txt
@@ -2613,19 +2613,25 @@ Hooking ``AdminSite`` instances into your URLconf
The last step in setting up the Django admin is to hook your ``AdminSite``
instance into your URLconf. Do this by pointing a given URL at the
-``AdminSite.urls`` method.
+``AdminSite.urls`` method. It is not necessary to use
+:func:`~django.conf.urls.include()`.
In this example, we register the default ``AdminSite`` instance
``django.contrib.admin.site`` at the URL ``/admin/`` ::
# urls.py
- from django.conf.urls import include, url
+ from django.conf.urls import url
from django.contrib import admin
urlpatterns = [
- url(r'^admin/', include(admin.site.urls)),
+ url(r'^admin/', admin.site.urls),
]
+.. versionchanged:: 1.9
+
+ In previous versions, you would pass ``admin.site.urls`` to
+ :func:`~django.conf.urls.include()`.
+
.. _customizing-adminsite:
Customizing the :class:`AdminSite` class
@@ -2655,12 +2661,12 @@ update :file:`myproject/urls.py` to reference your :class:`AdminSite` subclass.
.. snippet::
:filename: myproject/urls.py
- from django.conf.urls import include, url
+ from django.conf.urls import url
from myapp.admin import admin_site
urlpatterns = [
- url(r'^myadmin/', include(admin_site.urls)),
+ url(r'^myadmin/', admin_site.urls),
]
Note that you may not want autodiscovery of ``admin`` modules when using your
@@ -2684,12 +2690,12 @@ separate versions of the admin site -- using the ``AdminSite`` instances
respectively::
# urls.py
- from django.conf.urls import include, url
+ from django.conf.urls import url
from myproject.admin import basic_site, advanced_site
urlpatterns = [
- url(r'^basic-admin/', include(basic_site.urls)),
- url(r'^advanced-admin/', include(advanced_site.urls)),
+ url(r'^basic-admin/', basic_site.urls),
+ url(r'^advanced-admin/', advanced_site.urls),
]
``AdminSite`` instances take a single argument to their constructor, their
diff --git a/docs/ref/contrib/gis/tutorial.txt b/docs/ref/contrib/gis/tutorial.txt
index 05d58f0ef4..71e5304082 100644
--- a/docs/ref/contrib/gis/tutorial.txt
+++ b/docs/ref/contrib/gis/tutorial.txt
@@ -749,7 +749,7 @@ Next, edit your ``urls.py`` in the ``geodjango`` application folder as follows::
from django.contrib.gis import admin
urlpatterns = [
- url(r'^admin/', include(admin.site.urls)),
+ url(r'^admin/', admin.site.urls),
]
Create an admin user:
diff --git a/docs/ref/urls.txt b/docs/ref/urls.txt
index 222f41a76e..0cdd29eef3 100644
--- a/docs/ref/urls.txt
+++ b/docs/ref/urls.txt
@@ -130,6 +130,7 @@ include()
.. function:: include(module[, namespace=None, app_name=None])
include(pattern_list)
+ 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
@@ -137,9 +138,14 @@ include()
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 3-tuple containing such iterable plus the names of the
- application and instance namespaces.
+ URL patterns, a 2-tuple containing such iterable plus the names of the
+ application namespaces, or a 3-tuple containing the iterable and the names
+ of both the application and instance namespace.
:arg module: URLconf module (or module name)
:arg namespace: Instance namespace for the URL entries being included
@@ -154,6 +160,20 @@ 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.1. 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.1. 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.1. Specify the
+ application namespace or remove the instance namespace.
+
handler400
----------
diff --git a/docs/releases/1.9.txt b/docs/releases/1.9.txt
index 55906864ef..1054699bf9 100644
--- a/docs/releases/1.9.txt
+++ b/docs/releases/1.9.txt
@@ -385,6 +385,11 @@ URLs
* Regular expression lookaround assertions are now allowed in URL patterns.
+* The application namespace can now be set using an ``app_name`` attribute
+ on the included module or object. It can also be set by passing a 2-tuple
+ of (<list of patterns>, <application namespace>) as the first argument to
+ :func:`~django.conf.urls.include`.
+
Validators
^^^^^^^^^^
@@ -706,6 +711,40 @@ extending. This change necessitated a new template loader API. The old
Details about the new API can be found :ref:`in the template loader
documentation <custom-template-loaders>`.
+Passing a 3-tuple or an ``app_name`` to :func:`~django.conf.urls.include()`
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The instance namespace part of passing a tuple as the first argument has been
+replaced by passing the ``namespace`` argument to ``include()``. The
+``app_name`` argument to ``include()`` has been replaced by passing a 2-tuple,
+or passing an object or module with an ``app_name`` attribute.
+
+If the ``app_name`` is set in this new way, the ``namespace`` argument is no
+longer required. It will default to the value of ``app_name``.
+
+This change also means that the old way of including an ``AdminSite`` instance
+is deprecated. Instead, pass ``admin.site.urls`` directly to
+:func:`~django.conf.urls.url()`:
+
+.. snippet::
+ :filename: urls.py
+
+ from django.conf.urls import url
+ from django.contrib import admin
+
+ urlpatterns = [
+ url(r'^admin/', admin.site.urls),
+ ]
+
+URL application namespace required if setting an instance namespace
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+In the past, an instance namespace without an application namespace
+would serve the same purpose as the application namespace, but it was
+impossible to reverse the patterns if there was an application namespace
+with the same name. Includes that specify an instance namespace require that
+the included URLconf sets an application namespace.
+
Miscellaneous
~~~~~~~~~~~~~
diff --git a/docs/topics/http/urls.txt b/docs/topics/http/urls.txt
index 607efe7308..aec8b74c3d 100644
--- a/docs/topics/http/urls.txt
+++ b/docs/topics/http/urls.txt
@@ -716,8 +716,8 @@ displaying polls.
from django.conf.urls import include, url
urlpatterns = [
- url(r'^author-polls/', include('polls.urls', namespace='author-polls', app_name='polls')),
- url(r'^publisher-polls/', include('polls.urls', namespace='publisher-polls', app_name='polls')),
+ url(r'^author-polls/', include('polls.urls', namespace='author-polls')),
+ url(r'^publisher-polls/', include('polls.urls', namespace='publisher-polls')),
]
.. snippet::
@@ -727,6 +727,7 @@ displaying polls.
from . import views
+ app_name = 'polls'
urlpatterns = [
url(r'^$', views.IndexView.as_view(), name='index'),
url(r'^(?P<pk>\d+)/$', views.DetailView.as_view(), name='detail'),
@@ -778,25 +779,44 @@ declared last in ``urlpatterns``.
URL namespaces and included URLconfs
------------------------------------
-URL namespaces of included URLconfs can be specified in two ways.
+Application namespaces of included URLconfs can be specified in two ways.
-Firstly, you can provide the :term:`application <application namespace>` and
-:term:`instance <instance namespace>` namespaces as arguments to
-:func:`~django.conf.urls.include()` when you construct your URL patterns. For
-example,::
+Firstly, you can set an ``app_name`` attribute in the included URLconf module,
+at the same level as the ``urlpatterns`` attribute. You have to pass the actual
+module, or a string reference to the module, to
+:func:`~django.conf.urls.include`, not the list of ``urlpatterns`` itself.
- url(r'^polls/', include('polls.urls', namespace='author-polls', app_name='polls')),
+.. snippet::
+ :filename: polls/urls.py
+
+ from django.conf.urls import url
+
+ from . import views
+
+ app_name = 'polls'
+ urlpatterns = [
+ url(r'^$', views.IndexView.as_view(), name='index'),
+ url(r'^(?P<pk>\d+)/$', views.DetailView.as_view(), name='detail'),
+ ...
+ ]
+
+.. snippet::
+ :filename: urls.py
-This will include the URLs defined in ``polls.urls`` into the
-:term:`application namespace` ``'polls'``, with the :term:`instance namespace`
-``'author-polls'``.
+ from django.conf.urls import include, url
+
+ urlpatterns = [
+ url(r'^polls/', include('polls.urls')),
+ ]
+
+The URLs defined in ``polls.urls`` will have an application namespace ``polls``.
Secondly, you can include an object that contains embedded namespace data. If
you ``include()`` a list of :func:`~django.conf.urls.url` instances,
the URLs contained in that object will be added to the global namespace.
-However, you can also ``include()`` a 3-tuple containing::
+However, you can also ``include()`` a 2-tuple containing::
- (<list of url() instances>, <application namespace>, <instance namespace>)
+ (<list of url() instances>, <application namespace>)
For example::
@@ -804,25 +824,25 @@ For example::
from . import views
- polls_patterns = [
+ polls_patterns = ([
url(r'^$', views.IndexView.as_view(), name='index'),
url(r'^(?P<pk>\d+)/$', views.DetailView.as_view(), name='detail'),
- ]
+ ], 'polls')
+
+ url(r'^polls/', include(polls_patterns)),
- url(r'^polls/', include((polls_patterns, 'polls', 'author-polls'))),
+This will include the nominated URL patterns into the given application
+namespace.
-This will include the nominated URL patterns into the given application and
-instance namespace.
+The instance namespace can be specified using the ``namespace`` argument to
+:func:`~django.conf.urls.include`. If the instance namespace is not specified,
+it will default to the included URLconf's application namespace. This means
+it will also be the default instance for that namespace.
-For example, the Django admin is deployed as instances of
-:class:`~django.contrib.admin.AdminSite`. ``AdminSite`` objects have a ``urls``
-attribute: A 3-tuple that contains all the patterns in the corresponding admin
-site, plus the application namespace ``'admin'``, and the name of the admin
-instance. It is this ``urls`` attribute that you ``include()`` into your
-projects ``urlpatterns`` when you deploy an admin instance.
+.. versionchanged:: 1.9
-Be sure to pass a tuple to ``include()``. If you simply pass three arguments:
-``include(polls_patterns, 'polls', 'author-polls')``, Django won't throw an
-error but due to the signature of ``include()``, ``'polls'`` will be the
-instance namespace and ``'author-polls'`` will be the application namespace
-instead of vice versa.
+ In previous versions, you had to specify both the application namespace
+ and the instance namespace in a single place, either by passing them as
+ parameters to :func:`~django.conf.urls.include` or by including a 3-tuple
+ containing
+ ``(<list of url() instances>, <application namespace>, <instance namespace>)``.
diff --git a/docs/topics/i18n/translation.txt b/docs/topics/i18n/translation.txt
index 557204adc2..e52adf034a 100644
--- a/docs/topics/i18n/translation.txt
+++ b/docs/topics/i18n/translation.txt
@@ -1285,11 +1285,11 @@ prepend the current active language code to all url patterns defined within
url(r'^sitemap\.xml$', sitemap, name='sitemap_xml'),
]
- news_patterns = [
+ news_patterns = ([
url(r'^$', news_views.index, name='index'),
url(r'^category/(?P<slug>[\w-]+)/$', news_views.category, name='category'),
url(r'^(?P<slug>[\w-]+)/$', news_views.details, name='detail'),
- ]
+ ], 'news')
urlpatterns += i18n_patterns(
url(r'^about/$', about_views.main, name='about'),
@@ -1343,11 +1343,11 @@ URL patterns can also be marked translatable using the
url(r'^sitemap\.xml$', sitemap, name='sitemap_xml'),
]
- news_patterns = [
+ news_patterns = ([
url(r'^$', news_views.index, name='index'),
url(_(r'^category/(?P<slug>[\w-]+)/$'), news_views.category, name='category'),
url(r'^(?P<slug>[\w-]+)/$', news_views.details, name='detail'),
- ]
+ ], 'news')
urlpatterns += i18n_patterns(
url(_(r'^about/$'), about_views.main, name='about'),
diff --git a/tests/admin_changelist/urls.py b/tests/admin_changelist/urls.py
index 4da326ccc4..1f553a85a9 100644
--- a/tests/admin_changelist/urls.py
+++ b/tests/admin_changelist/urls.py
@@ -1,7 +1,7 @@
-from django.conf.urls import include, url
+from django.conf.urls import url
from . import admin
urlpatterns = [
- url(r'^admin/', include(admin.site.urls)),
+ url(r'^admin/', admin.site.urls),
]
diff --git a/tests/admin_custom_urls/urls.py b/tests/admin_custom_urls/urls.py
index 103f9b0121..b07e1395b9 100644
--- a/tests/admin_custom_urls/urls.py
+++ b/tests/admin_custom_urls/urls.py
@@ -1,7 +1,7 @@
-from django.conf.urls import include, url
+from django.conf.urls import url
from .models import site
urlpatterns = [
- url(r'^admin/', include(site.urls)),
+ url(r'^admin/', site.urls),
]
diff --git a/tests/admin_docs/urls.py b/tests/admin_docs/urls.py
index 3fbfaff0c4..0bcdee4b4b 100644
--- a/tests/admin_docs/urls.py
+++ b/tests/admin_docs/urls.py
@@ -3,12 +3,12 @@ from django.contrib import admin
from . import views
-ns_patterns = [
+ns_patterns = ([
url(r'^xview/func/$', views.xview_dec(views.xview), name='func'),
-]
+], 'test')
urlpatterns = [
- url(r'^admin/', include(admin.site.urls)),
+ url(r'^admin/', admin.site.urls),
url(r'^admindocs/', include('django.contrib.admindocs.urls')),
url(r'^', include(ns_patterns, namespace='test')),
url(r'^xview/func/$', views.xview_dec(views.xview)),
diff --git a/tests/admin_inlines/urls.py b/tests/admin_inlines/urls.py
index 4da326ccc4..1f553a85a9 100644
--- a/tests/admin_inlines/urls.py
+++ b/tests/admin_inlines/urls.py
@@ -1,7 +1,7 @@
-from django.conf.urls import include, url
+from django.conf.urls import url
from . import admin
urlpatterns = [
- url(r'^admin/', include(admin.site.urls)),
+ url(r'^admin/', admin.site.urls),
]
diff --git a/tests/admin_views/test_adminsite.py b/tests/admin_views/test_adminsite.py
index 6e0fce57d8..575a15c23a 100644
--- a/tests/admin_views/test_adminsite.py
+++ b/tests/admin_views/test_adminsite.py
@@ -2,7 +2,7 @@ from __future__ import unicode_literals
import datetime
-from django.conf.urls import include, url
+from django.conf.urls import url
from django.contrib import admin
from django.contrib.auth.models import User
from django.core.urlresolvers import reverse
@@ -16,7 +16,7 @@ site.register(User)
site.register(Article)
urlpatterns = [
- url(r'^test_admin/admin/', include(site.urls)),
+ url(r'^test_admin/admin/', site.urls),
]
diff --git a/tests/admin_views/urls.py b/tests/admin_views/urls.py
index 679ae916af..926eca0a2d 100644
--- a/tests/admin_views/urls.py
+++ b/tests/admin_views/urls.py
@@ -6,11 +6,11 @@ urlpatterns = [
url(r'^test_admin/admin/doc/', include('django.contrib.admindocs.urls')),
url(r'^test_admin/admin/secure-view/$', views.secure_view, name='secure_view'),
url(r'^test_admin/admin/secure-view2/$', views.secure_view2, name='secure_view2'),
- url(r'^test_admin/admin/', include(admin.site.urls)),
- url(r'^test_admin/admin2/', include(customadmin.site.urls)),
- url(r'^test_admin/admin3/', include(admin.site.get_urls(), 'admin3', 'admin'), dict(form_url='pony')),
- url(r'^test_admin/admin4/', include(customadmin.simple_site.urls)),
- url(r'^test_admin/admin5/', include(admin.site2.urls)),
- url(r'^test_admin/admin7/', include(admin.site7.urls)),
- url(r'^test_admin/has_permission_admin/', include(custom_has_permission_admin.site.urls)),
+ url(r'^test_admin/admin/', admin.site.urls),
+ url(r'^test_admin/admin2/', customadmin.site.urls),
+ url(r'^test_admin/admin3/', (admin.site.get_urls(), 'admin', 'admin3'), dict(form_url='pony')),
+ url(r'^test_admin/admin4/', customadmin.simple_site.urls),
+ url(r'^test_admin/admin5/', admin.site2.urls),
+ url(r'^test_admin/admin7/', admin.site7.urls),
+ url(r'^test_admin/has_permission_admin/', custom_has_permission_admin.site.urls),
]
diff --git a/tests/admin_widgets/urls.py b/tests/admin_widgets/urls.py
index 7f4721c57e..3381b2f13a 100644
--- a/tests/admin_widgets/urls.py
+++ b/tests/admin_widgets/urls.py
@@ -1,7 +1,7 @@
-from django.conf.urls import include, url
+from django.conf.urls import url
from . import widgetadmin
urlpatterns = [
- url(r'^', include(widgetadmin.site.urls)),
+ url(r'^', widgetadmin.site.urls),
]
diff --git a/tests/auth_tests/urls.py b/tests/auth_tests/urls.py
index 3c4dc6b12b..2851b451e1 100644
--- a/tests/auth_tests/urls.py
+++ b/tests/auth_tests/urls.py
@@ -1,4 +1,4 @@
-from django.conf.urls import include, url
+from django.conf.urls import url
from django.contrib import admin
from django.contrib.auth import views
from django.contrib.auth.decorators import login_required
@@ -97,5 +97,5 @@ urlpatterns = auth_urlpatterns + [
url(r'^userpage/(.+)/$', userpage, name="userpage"),
# This line is only required to render the password reset with is_admin=True
- url(r'^admin/', include(admin.site.urls)),
+ url(r'^admin/', admin.site.urls),
]
diff --git a/tests/auth_tests/urls_admin.py b/tests/auth_tests/urls_admin.py
index 8d2fe3fd10..8e5b0f1f0c 100644
--- a/tests/auth_tests/urls_admin.py
+++ b/tests/auth_tests/urls_admin.py
@@ -2,7 +2,7 @@
Test URLs for auth admins.
"""
-from django.conf.urls import include, url
+from django.conf.urls import url
from django.contrib import admin
from django.contrib.auth.admin import GroupAdmin, UserAdmin
from django.contrib.auth.models import Group, User
@@ -14,5 +14,5 @@ site.register(User, UserAdmin)
site.register(Group, GroupAdmin)
urlpatterns += [
- url(r'^admin/', include(site.urls)),
+ url(r'^admin/', site.urls),
]
diff --git a/tests/auth_tests/urls_custom_user_admin.py b/tests/auth_tests/urls_custom_user_admin.py
index f8bc194ba5..dc47be68c7 100644
--- a/tests/auth_tests/urls_custom_user_admin.py
+++ b/tests/auth_tests/urls_custom_user_admin.py
@@ -1,4 +1,4 @@
-from django.conf.urls import include, url
+from django.conf.urls import url
from django.contrib import admin
from django.contrib.auth import get_user_model
from django.contrib.auth.admin import UserAdmin
@@ -16,5 +16,5 @@ class CustomUserAdmin(UserAdmin):
site.register(get_user_model(), CustomUserAdmin)
urlpatterns = [
- url(r'^admin/', include(site.urls)),
+ url(r'^admin/', site.urls),
]
diff --git a/tests/generic_inline_admin/urls.py b/tests/generic_inline_admin/urls.py
index bbabfc21a4..59f09437db 100644
--- a/tests/generic_inline_admin/urls.py
+++ b/tests/generic_inline_admin/urls.py
@@ -1,7 +1,7 @@
-from django.conf.urls import include, url
+from django.conf.urls import url
from . import admin
urlpatterns = [
- url(r'^generic_inline_admin/admin/', include(admin.site.urls)),
+ url(r'^generic_inline_admin/admin/', admin.site.urls),
]
diff --git a/tests/i18n/patterns/urls/namespace.py b/tests/i18n/patterns/urls/namespace.py
index 6060c57bd6..3a34c7d815 100644
--- a/tests/i18n/patterns/urls/namespace.py
+++ b/tests/i18n/patterns/urls/namespace.py
@@ -4,6 +4,7 @@ from django.views.generic import TemplateView
view = TemplateView.as_view(template_name='dummy.html')
+app_name = 'account'
urlpatterns = [
url(_(r'^register/$'), view, name='register'),
url(_(r'^register-without-slash$'), view, name='register-without-slash'),
diff --git a/tests/i18n/patterns/urls/wrong_namespace.py b/tests/i18n/patterns/urls/wrong_namespace.py
index e19c99c935..3983cb6195 100644
--- a/tests/i18n/patterns/urls/wrong_namespace.py
+++ b/tests/i18n/patterns/urls/wrong_namespace.py
@@ -5,6 +5,7 @@ from django.views.generic import TemplateView
view = TemplateView.as_view(template_name='dummy.html')
+app_name = 'account'
urlpatterns = i18n_patterns(
url(_(r'^register/$'), view, name='register'),
)
diff --git a/tests/proxy_models/urls.py b/tests/proxy_models/urls.py
index 854ac78040..18ade2e739 100644
--- a/tests/proxy_models/urls.py
+++ b/tests/proxy_models/urls.py
@@ -1,7 +1,7 @@
-from django.conf.urls import include, url
+from django.conf.urls import url
from .admin import site
urlpatterns = [
- url(r'^admin/', include(site.urls)),
+ url(r'^admin/', site.urls),
]
diff --git a/tests/timezones/urls.py b/tests/timezones/urls.py
index e5c88a0025..84b13b593d 100644
--- a/tests/timezones/urls.py
+++ b/tests/timezones/urls.py
@@ -1,7 +1,7 @@
-from django.conf.urls import include, url
+from django.conf.urls import url
from . import admin as tz_admin # NOQA: register tz_admin
urlpatterns = [
- url(r'^admin/', include(tz_admin.site.urls)),
+ url(r'^admin/', tz_admin.site.urls),
]
diff --git a/tests/urlpatterns_reverse/included_app_urls.py b/tests/urlpatterns_reverse/included_app_urls.py
new file mode 100644
index 0000000000..570d6fc518
--- /dev/null
+++ b/tests/urlpatterns_reverse/included_app_urls.py
@@ -0,0 +1,16 @@
+from django.conf.urls import url
+
+from . import views
+
+app_name = 'inc-app'
+urlpatterns = [
+ url(r'^normal/$', views.empty_view, name='inc-normal-view'),
+ url(r'^normal/(?P<arg1>[0-9]+)/(?P<arg2>[0-9]+)/$', views.empty_view, name='inc-normal-view'),
+
+ url(r'^\+\\\$\*/$', views.empty_view, name='inc-special-view'),
+
+ url(r'^mixed_args/([0-9]+)/(?P<arg2>[0-9]+)/$', views.empty_view, name='inc-mixed-args'),
+ url(r'^no_kwargs/([0-9]+)/([0-9]+)/$', views.empty_view, name='inc-no-kwargs'),
+
+ url(r'^view_class/(?P<arg1>[0-9]+)/(?P<arg2>[0-9]+)/$', views.view_class_instance, name='inc-view-class'),
+]
diff --git a/tests/urlpatterns_reverse/namespace_urls.py b/tests/urlpatterns_reverse/namespace_urls.py
index 9efd0e9d64..460778860f 100644
--- a/tests/urlpatterns_reverse/namespace_urls.py
+++ b/tests/urlpatterns_reverse/namespace_urls.py
@@ -1,20 +1,7 @@
from django.conf.urls import include, url
from . import views
-
-
-class URLObject(object):
- def __init__(self, app_name, namespace):
- self.app_name = app_name
- self.namespace = namespace
-
- def urls(self):
- return ([
- url(r'^inner/$', views.empty_view, name='urlobject-view'),
- url(r'^inner/(?P<arg1>[0-9]+)/(?P<arg2>[0-9]+)/$', views.empty_view, name='urlobject-view'),
- url(r'^inner/\+\\\$\*/$', views.empty_view, name='urlobject-special-view'),
- ], self.app_name, self.namespace)
- urls = property(urls)
+from .tests import URLObject
testobj1 = URLObject('testapp', 'test-ns1')
testobj2 = URLObject('testapp', 'test-ns2')
@@ -23,6 +10,8 @@ default_testobj = URLObject('testapp', 'testapp')
otherobj1 = URLObject('nodefault', 'other-ns1')
otherobj2 = URLObject('nodefault', 'other-ns2')
+newappobj1 = URLObject('newapp')
+
urlpatterns = [
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'),
@@ -45,6 +34,12 @@ urlpatterns = [
url(r'^other1/', include(otherobj1.urls)),
url(r'^other[246]/', include(otherobj2.urls)),
+ url(r'^newapp1/', include(newappobj1.app_urls, 'new-ns1')),
+ url(r'^new-default/', include(newappobj1.app_urls)),
+
+ url(r'^app-included[135]/', include('urlpatterns_reverse.included_app_urls', namespace='app-ns1')),
+ url(r'^app-included2/', include('urlpatterns_reverse.included_app_urls', namespace='app-ns2')),
+
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')),
diff --git a/tests/urlpatterns_reverse/tests.py b/tests/urlpatterns_reverse/tests.py
index c9378c784e..a8aa0b2e1b 100644
--- a/tests/urlpatterns_reverse/tests.py
+++ b/tests/urlpatterns_reverse/tests.py
@@ -10,7 +10,7 @@ import unittest
from admin_scripts.tests import AdminScriptTestCase
from django.conf import settings
-from django.conf.urls import include
+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 (
@@ -26,7 +26,9 @@ from django.test import (
)
from django.test.utils import override_script_prefix
from django.utils import six
-from django.utils.deprecation import RemovedInDjango20Warning
+from django.utils.deprecation import (
+ RemovedInDjango20Warning, RemovedInDjango21Warning,
+)
from . import middleware, urlconf_outer, views
from .views import empty_view
@@ -184,6 +186,26 @@ test_data = (
)
+class URLObject(object):
+ urlpatterns = [
+ url(r'^inner/$', views.empty_view, name='urlobject-view'),
+ url(r'^inner/(?P<arg1>[0-9]+)/(?P<arg2>[0-9]+)/$', views.empty_view, name='urlobject-view'),
+ url(r'^inner/\+\\\$\*/$', views.empty_view, name='urlobject-special-view'),
+ ]
+
+ def __init__(self, app_name, namespace=None):
+ self.app_name = app_name
+ self.namespace = namespace
+
+ @property
+ def urls(self):
+ return self.urlpatterns, self.app_name, self.namespace
+
+ @property
+ def app_urls(self):
+ return self.urlpatterns, self.app_name
+
+
@override_settings(ROOT_URLCONF='urlpatterns_reverse.no_urls')
class NoURLPatternsTests(SimpleTestCase):
@@ -281,6 +303,7 @@ class URLPatternReverse(SimpleTestCase):
class ResolverTests(unittest.TestCase):
+ @ignore_warnings(category=RemovedInDjango21Warning)
def test_resolver_repr(self):
"""
Test repr of RegexURLResolver, especially when urlconf_name is a list
@@ -460,6 +483,7 @@ class ReverseShortcutTests(SimpleTestCase):
@override_settings(ROOT_URLCONF='urlpatterns_reverse.namespace_urls')
+@ignore_warnings(category=RemovedInDjango21Warning)
class NamespaceTests(SimpleTestCase):
def test_ambiguous_object(self):
@@ -500,6 +524,20 @@ class NamespaceTests(SimpleTestCase):
self.assertEqual('/test1/inner/42/37/', reverse('test-ns1:urlobject-view', kwargs={'arg1': 42, 'arg2': 37}))
self.assertEqual('/test1/inner/+%5C$*/', reverse('test-ns1:urlobject-special-view'))
+ def test_app_object(self):
+ "Dynamic URL objects can return a (pattern, app_name) 2-tuple, and include() can set the namespace"
+ self.assertEqual('/newapp1/inner/', reverse('new-ns1:urlobject-view'))
+ self.assertEqual('/newapp1/inner/37/42/', reverse('new-ns1:urlobject-view', args=[37, 42]))
+ self.assertEqual('/newapp1/inner/42/37/', reverse('new-ns1:urlobject-view', kwargs={'arg1': 42, 'arg2': 37}))
+ self.assertEqual('/newapp1/inner/+%5C$*/', reverse('new-ns1:urlobject-special-view'))
+
+ def test_app_object_default_namespace(self):
+ "Namespace defaults to app_name when including a (pattern, app_name) 2-tuple"
+ self.assertEqual('/new-default/inner/', reverse('newapp:urlobject-view'))
+ self.assertEqual('/new-default/inner/37/42/', reverse('newapp:urlobject-view', args=[37, 42]))
+ self.assertEqual('/new-default/inner/42/37/', reverse('newapp:urlobject-view', kwargs={'arg1': 42, 'arg2': 37}))
+ self.assertEqual('/new-default/inner/+%5C$*/', reverse('newapp:urlobject-special-view'))
+
def test_embedded_namespace_object(self):
"Namespaces can be installed anywhere in the URL pattern tree"
self.assertEqual('/included/test3/inner/', reverse('test-ns3:urlobject-view'))
@@ -514,6 +552,13 @@ class NamespaceTests(SimpleTestCase):
self.assertEqual('/ns-included1/normal/42/37/', reverse('inc-ns1:inc-normal-view', kwargs={'arg1': 42, 'arg2': 37}))
self.assertEqual('/ns-included1/+%5C$*/', reverse('inc-ns1:inc-special-view'))
+ def test_app_name_pattern(self):
+ "Namespaces can be applied to include()'d urlpatterns that set an app_name attribute"
+ self.assertEqual('/app-included1/normal/', reverse('app-ns1:inc-normal-view'))
+ self.assertEqual('/app-included1/normal/37/42/', reverse('app-ns1:inc-normal-view', args=[37, 42]))
+ self.assertEqual('/app-included1/normal/42/37/', reverse('app-ns1:inc-normal-view', kwargs={'arg1': 42, 'arg2': 37}))
+ self.assertEqual('/app-included1/+%5C$*/', reverse('app-ns1:inc-special-view'))
+
def test_namespace_pattern_with_variable_prefix(self):
"When using an include with namespaces when there is a regex variable in front of it"
self.assertEqual('/ns-outer/42/normal/', reverse('inc-outer:inc-normal-view', kwargs={'outer': 42}))
@@ -769,6 +814,7 @@ class NoRootUrlConfTests(SimpleTestCase):
@override_settings(ROOT_URLCONF='urlpatterns_reverse.namespace_urls')
class ResolverMatchTests(SimpleTestCase):
+ @ignore_warnings(category=RemovedInDjango21Warning)
def test_urlpattern_resolve(self):
for path, url_name, app_name, namespace, view_name, func, args, kwargs in resolve_test_data:
# Test legacy support for extracting "function, args, kwargs"
@@ -793,6 +839,7 @@ class ResolverMatchTests(SimpleTestCase):
self.assertEqual(match[1], args)
self.assertEqual(match[2], kwargs)
+ @ignore_warnings(category=RemovedInDjango21Warning)
def test_resolver_match_on_request(self):
response = self.client.get('/resolver_match/')
resolver_match = response.resolver_match
@@ -845,10 +892,65 @@ class ViewLoadingTests(SimpleTestCase):
class IncludeTests(SimpleTestCase):
+ url_patterns = [
+ url(r'^inner/$', views.empty_view, name='urlobject-view'),
+ url(r'^inner/(?P<arg1>[0-9]+)/(?P<arg2>[0-9]+)/$', views.empty_view, name='urlobject-view'),
+ url(r'^inner/\+\\\$\*/$', views.empty_view, name='urlobject-special-view'),
+ ]
+ 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('urls', app_name='bar')
+ include(self.url_patterns, app_name='bar')
+
+ def test_include_urls(self):
+ self.assertEqual(include(self.url_patterns), (self.url_patterns, None, None))
+
+ @ignore_warnings(category=RemovedInDjango21Warning)
+ def test_include_namespace(self):
+ # no app_name -> deprecated
+ self.assertEqual(include(self.url_patterns, 'namespace'), (self.url_patterns, None, 'namespace'))
+
+ @ignore_warnings(category=RemovedInDjango21Warning)
+ 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')
+ )
+
+ @ignore_warnings(category=RemovedInDjango21Warning)
+ def test_include_3_tuple(self):
+ # 3-tuple -> deprecated
+ self.assertEqual(
+ include((self.url_patterns, 'app_name', 'namespace')),
+ (self.url_patterns, 'app_name', 'namespace')
+ )
+
+ def test_include_2_tuple(self):
+ self.assertEqual(
+ include((self.url_patterns, 'app_name')),
+ (self.url_patterns, 'app_name', 'app_name')
+ )
+
+ def test_include_2_tuple_namespace(self):
+ self.assertEqual(
+ include((self.url_patterns, 'app_name'), namespace='namespace'),
+ (self.url_patterns, 'app_name', 'namespace')
+ )
+
+ def test_include_app_name(self):
+ self.assertEqual(
+ include(self.app_urls),
+ (self.app_urls, 'inc-app', 'inc-app')
+ )
+
+ def test_include_app_name_namespace(self):
+ self.assertEqual(
+ include(self.app_urls, 'namespace'),
+ (self.app_urls, 'inc-app', 'namespace')
+ )
@override_settings(ROOT_URLCONF='urlpatterns_reverse.urls')
diff --git a/tests/view_tests/default_urls.py b/tests/view_tests/default_urls.py
index 2811bf7740..f23a286305 100644
--- a/tests/view_tests/default_urls.py
+++ b/tests/view_tests/default_urls.py
@@ -1,7 +1,7 @@
-from django.conf.urls import include, url
+from django.conf.urls import url
from django.contrib import admin
urlpatterns = [
# This is the same as in the default project template
- url(r'^admin/', include(admin.site.urls)),
+ url(r'^admin/', admin.site.urls),
]