From a9fd740d22bc4fed5fdb280c036618000ee13df1 Mon Sep 17 00:00:00 2001 From: Tim Graham Date: Tue, 12 Aug 2014 10:54:42 -0400 Subject: [PATCH] Fixed #23276 -- Deprecated passing views as strings to url(). --- django/conf/urls/__init__.py | 6 + django/conf/urls/i18n.py | 3 +- django/conf/urls/static.py | 3 +- django/contrib/auth/tests/urls.py | 32 +++--- django/contrib/staticfiles/urls.py | 3 +- django/contrib/staticfiles/views.py | 4 +- django/views/static.py | 4 +- docs/internals/deprecation.txt | 2 + docs/intro/overview.txt | 8 +- docs/ref/contrib/admin/index.txt | 10 +- docs/ref/contrib/sitemaps.txt | 24 ++-- docs/ref/urls.txt | 18 ++- docs/releases/1.8.txt | 13 ++- docs/topics/http/urls.txt | 117 +++++++------------- docs/topics/i18n/translation.txt | 30 +++-- tests/admin_scripts/urls.py | 3 +- tests/generic_views/urls.py | 3 +- tests/middleware/extra_urls.py | 8 +- tests/middleware/urls.py | 8 +- tests/model_permalink/urls.py | 4 +- tests/template_tests/urls.py | 2 +- tests/test_client/urls.py | 5 +- tests/urlpatterns_reverse/erroneous_urls.py | 45 ++++---- tests/urlpatterns_reverse/namespace_urls.py | 6 +- tests/urlpatterns_reverse/urls.py | 11 +- tests/view_tests/generic_urls.py | 9 +- tests/view_tests/regression_21530_urls.py | 4 +- tests/view_tests/urls.py | 4 +- 28 files changed, 207 insertions(+), 182 deletions(-) diff --git a/django/conf/urls/__init__.py b/django/conf/urls/__init__.py index 60c767fc9b..094b4cb3e8 100644 --- a/django/conf/urls/__init__.py +++ b/django/conf/urls/__init__.py @@ -67,6 +67,12 @@ def url(regex, view, kwargs=None, name=None, prefix=''): return RegexURLResolver(regex, urlconf_module, kwargs, app_name=app_name, namespace=namespace) else: if isinstance(view, six.string_types): + warnings.warn( + 'Support for string view arguments to url() is deprecated and ' + 'will be removed in Django 2.0 (got %s). Pass the callable ' + 'instead.' % view, + RemovedInDjango20Warning, stacklevel=2 + ) if not view: raise ImproperlyConfigured('Empty URL pattern view name not permitted (for pattern %r)' % regex) if prefix: diff --git a/django/conf/urls/i18n.py b/django/conf/urls/i18n.py index 6e776a2ecd..9366a5f708 100644 --- a/django/conf/urls/i18n.py +++ b/django/conf/urls/i18n.py @@ -5,6 +5,7 @@ from django.conf.urls import patterns, url from django.core.urlresolvers import LocaleRegexURLResolver from django.utils import six from django.utils.deprecation import RemovedInDjango20Warning +from django.views.i18n import set_language def i18n_patterns(prefix, *args): @@ -30,5 +31,5 @@ def i18n_patterns(prefix, *args): urlpatterns = [ - url(r'^setlang/$', 'django.views.i18n.set_language', name='set_language'), + url(r'^setlang/$', set_language, name='set_language'), ] diff --git a/django/conf/urls/static.py b/django/conf/urls/static.py index f3470af025..ab4270b0a5 100644 --- a/django/conf/urls/static.py +++ b/django/conf/urls/static.py @@ -3,9 +3,10 @@ import re from django.conf import settings from django.conf.urls import url from django.core.exceptions import ImproperlyConfigured +from django.views.static import serve -def static(prefix, view='django.views.static.serve', **kwargs): +def static(prefix, view=serve, **kwargs): """ Helper function to return a URL pattern for serving files in debug mode. diff --git a/django/contrib/auth/tests/urls.py b/django/contrib/auth/tests/urls.py index 9997b29bbe..bad129cd5e 100644 --- a/django/contrib/auth/tests/urls.py +++ b/django/contrib/auth/tests/urls.py @@ -3,7 +3,7 @@ from django.contrib import admin from django.contrib.auth import context_processors from django.contrib.auth.forms import AuthenticationForm from django.contrib.auth.urls import urlpatterns -from django.contrib.auth.views import password_reset, login +from django.contrib.auth import views from django.contrib.auth.decorators import login_required from django.contrib.messages.api import info from django.http import HttpResponse, HttpRequest @@ -67,29 +67,29 @@ def userpage(request): def custom_request_auth_login(request): - return login(request, authentication_form=CustomRequestAuthenticationForm) + return views.login(request, authentication_form=CustomRequestAuthenticationForm) # special urls for auth test cases urlpatterns += [ - url(r'^logout/custom_query/$', 'django.contrib.auth.views.logout', dict(redirect_field_name='follow')), - url(r'^logout/next_page/$', 'django.contrib.auth.views.logout', dict(next_page='/somewhere/')), - url(r'^logout/next_page/named/$', 'django.contrib.auth.views.logout', dict(next_page='password_reset')), + url(r'^logout/custom_query/$', views.logout, dict(redirect_field_name='follow')), + url(r'^logout/next_page/$', views.logout, dict(next_page='/somewhere/')), + url(r'^logout/next_page/named/$', views.logout, dict(next_page='password_reset')), url(r'^remote_user/$', remote_user_auth_view), - url(r'^password_reset_from_email/$', 'django.contrib.auth.views.password_reset', dict(from_email='staffmember@example.com')), - url(r'^password_reset/custom_redirect/$', 'django.contrib.auth.views.password_reset', dict(post_reset_redirect='/custom/')), - url(r'^password_reset/custom_redirect/named/$', 'django.contrib.auth.views.password_reset', dict(post_reset_redirect='password_reset')), - url(r'^password_reset/html_email_template/$', 'django.contrib.auth.views.password_reset', dict(html_email_template_name='registration/html_password_reset_email.html')), + url(r'^password_reset_from_email/$', views.password_reset, dict(from_email='staffmember@example.com')), + url(r'^password_reset/custom_redirect/$', views.password_reset, dict(post_reset_redirect='/custom/')), + url(r'^password_reset/custom_redirect/named/$', views.password_reset, dict(post_reset_redirect='password_reset')), + url(r'^password_reset/html_email_template/$', views.password_reset, dict(html_email_template_name='registration/html_password_reset_email.html')), url(r'^reset/custom/(?P[0-9A-Za-z_\-]+)/(?P[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$', - 'django.contrib.auth.views.password_reset_confirm', + views.password_reset_confirm, dict(post_reset_redirect='/custom/')), url(r'^reset/custom/named/(?P[0-9A-Za-z_\-]+)/(?P[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$', - 'django.contrib.auth.views.password_reset_confirm', + views.password_reset_confirm, dict(post_reset_redirect='password_reset')), - url(r'^password_change/custom/$', 'django.contrib.auth.views.password_change', dict(post_change_redirect='/custom/')), - url(r'^password_change/custom/named/$', 'django.contrib.auth.views.password_change', dict(post_change_redirect='password_reset')), - url(r'^admin_password_reset/$', 'django.contrib.auth.views.password_reset', dict(is_admin_site=True)), - url(r'^login_required/$', login_required(password_reset)), - url(r'^login_required_login_url/$', login_required(password_reset, login_url='/somewhere/')), + url(r'^password_change/custom/$', views.password_change, dict(post_change_redirect='/custom/')), + url(r'^password_change/custom/named/$', views.password_change, dict(post_change_redirect='password_reset')), + url(r'^admin_password_reset/$', views.password_reset, dict(is_admin_site=True)), + url(r'^login_required/$', login_required(views.password_reset)), + url(r'^login_required_login_url/$', login_required(views.password_reset, login_url='/somewhere/')), url(r'^auth_processor_no_attr_access/$', auth_processor_no_attr_access), url(r'^auth_processor_attr_access/$', auth_processor_attr_access), diff --git a/django/contrib/staticfiles/urls.py b/django/contrib/staticfiles/urls.py index 4a0feeaaf8..5f27f4f14d 100644 --- a/django/contrib/staticfiles/urls.py +++ b/django/contrib/staticfiles/urls.py @@ -1,5 +1,6 @@ from django.conf import settings from django.conf.urls.static import static +from django.contrib.staticfiles.views import serve urlpatterns = [] @@ -10,7 +11,7 @@ def staticfiles_urlpatterns(prefix=None): """ if prefix is None: prefix = settings.STATIC_URL - return static(prefix, view='django.contrib.staticfiles.views.serve') + return static(prefix, view=serve) # Only append if urlpatterns are empty if settings.DEBUG and not urlpatterns: diff --git a/django/contrib/staticfiles/views.py b/django/contrib/staticfiles/views.py index 5312de2931..166879a0e5 100644 --- a/django/contrib/staticfiles/views.py +++ b/django/contrib/staticfiles/views.py @@ -21,7 +21,9 @@ def serve(request, path, insecure=False, **kwargs): To use, put a URL pattern such as:: - (r'^(?P.*)$', 'django.contrib.staticfiles.views.serve') + from django.contrib.staticfiles import views + + url(r'^(?P.*)$', views.serve) in your URLconf. diff --git a/django/views/static.py b/django/views/static.py index 68fb7c4654..2998688284 100644 --- a/django/views/static.py +++ b/django/views/static.py @@ -24,7 +24,9 @@ def serve(request, path, document_root=None, show_indexes=False): To use, put a URL pattern such as:: - (r'^(?P.*)$', 'django.views.static.serve', {'document_root': '/path/to/my/files/'}) + from django.views.static import serve + + url(r'^(?P.*)$', serve, {'document_root': '/path/to/my/files/'}) in your URLconf. You must provide the ``document_root`` param. You may also set ``show_indexes`` to ``True`` if you'd like to serve a basic index diff --git a/docs/internals/deprecation.txt b/docs/internals/deprecation.txt index 2adf9cd199..ee5e9d0f67 100644 --- a/docs/internals/deprecation.txt +++ b/docs/internals/deprecation.txt @@ -44,6 +44,8 @@ about each item can often be found in the release notes of two versions prior. * The ``unordered_list`` filter will no longer support old style lists. +* Support for string ``view`` arguments to ``url()`` will be removed. + .. _deprecation-removed-in-1.9: 1.9 diff --git a/docs/intro/overview.txt b/docs/intro/overview.txt index 685666f8cb..c60880be5c 100644 --- a/docs/intro/overview.txt +++ b/docs/intro/overview.txt @@ -186,10 +186,12 @@ example above:: from django.conf.urls import url + from . import views + urlpatterns = [ - url(r'^articles/([0-9]{4})/$', 'news.views.year_archive'), - url(r'^articles/([0-9]{4})/([0-9]{2})/$', 'news.views.month_archive'), - url(r'^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$', 'news.views.article_detail'), + url(r'^articles/([0-9]{4})/$', views.year_archive), + url(r'^articles/([0-9]{4})/([0-9]{2})/$', views.month_archive), + url(r'^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$', views.article_detail), ] The code above maps URLs, as simple `regular expressions`_, to the location of diff --git a/docs/ref/contrib/admin/index.txt b/docs/ref/contrib/admin/index.txt index 8d69ac6e82..26c43b9743 100644 --- a/docs/ref/contrib/admin/index.txt +++ b/docs/ref/contrib/admin/index.txt @@ -2571,10 +2571,12 @@ your URLconf. Specifically, add these four patterns: .. code-block:: python - url(r'^admin/password_reset/$', 'django.contrib.auth.views.password_reset', name='admin_password_reset'), - url(r'^admin/password_reset/done/$', 'django.contrib.auth.views.password_reset_done', name='password_reset_done'), - url(r'^reset/(?P[0-9A-Za-z_\-]+)/(?P.+)/$', 'django.contrib.auth.views.password_reset_confirm', name='password_reset_confirm'), - url(r'^reset/done/$', 'django.contrib.auth.views.password_reset_complete', name='password_reset_complete'), + from django.contrib.auth import views as auth_views + + url(r'^admin/password_reset/$', auth_views.password_reset, name='admin_password_reset'), + url(r'^admin/password_reset/done/$', auth_views.password_reset_done, name='password_reset_done'), + url(r'^reset/(?P[0-9A-Za-z_\-]+)/(?P.+)/$', auth_views.password_reset_confirm, name='password_reset_confirm'), + url(r'^reset/done/$', auth_views.password_reset_complete, name='password_reset_complete'), (This assumes you've added the admin at ``admin/`` and requires that you put the URLs starting with ``^admin/`` before the line that includes the admin app diff --git a/docs/ref/contrib/sitemaps.txt b/docs/ref/contrib/sitemaps.txt index 42552e8733..f8a21f3d01 100644 --- a/docs/ref/contrib/sitemaps.txt +++ b/docs/ref/contrib/sitemaps.txt @@ -54,8 +54,10 @@ Initialization To activate sitemap generation on your Django site, add this line to your :doc:`URLconf `:: - url(r'^sitemap\.xml$', 'django.contrib.sitemaps.views.sitemap', - {'sitemaps': sitemaps}, name='django.contrib.sitemaps.views.sitemap') + from django.contrib.sitemaps.views import sitemap + + url(r'^sitemap\.xml$', sitemap, {'sitemaps': sitemaps}, + name='django.contrib.sitemaps.views.sitemap') This tells Django to build a sitemap when a client accesses :file:`/sitemap.xml`. @@ -277,6 +279,7 @@ Here's an example of a :doc:`URLconf ` using both:: from django.conf.urls import url from django.contrib.sitemaps import FlatPageSitemap, GenericSitemap + from django.contrib.sitemaps.views import sitemap from blog.models import Entry info_dict = { @@ -294,8 +297,8 @@ Here's an example of a :doc:`URLconf ` using both:: # ... # the sitemap - url(r'^sitemap\.xml$', 'django.contrib.sitemaps.views.sitemap', - {'sitemaps': sitemaps}, name='django.contrib.sitemaps.views.sitemap'), + url(r'^sitemap\.xml$', sitemap, {'sitemaps': sitemaps}, + name='django.contrib.sitemaps.views.sitemap'), ] .. _URLconf: ../url_dispatch/ @@ -311,8 +314,11 @@ the sitemap. For example:: # sitemaps.py from django.contrib import sitemaps + from django.contrib.sitemaps.views import sitemap from django.core.urlresolvers import reverse + from . import views + class StaticViewSitemap(sitemaps.Sitemap): priority = 0.5 changefreq = 'daily' @@ -332,12 +338,12 @@ the sitemap. For example:: } urlpatterns = [ - url(r'^$', 'views.main', name='main'), - url(r'^about/$', 'views.about', name='about'), - url(r'^license/$', 'views.license', name='license'), + url(r'^$', views.main, name='main'), + url(r'^about/$', views.about, name='about'), + url(r'^license/$', views.license, name='license'), # ... - url(r'^sitemap\.xml$', 'django.contrib.sitemaps.views.sitemap', - {'sitemaps': sitemaps}, name='django.contrib.sitemaps.views.sitemap') + url(r'^sitemap\.xml$', sitemap, {'sitemaps': sitemaps}, + name='django.contrib.sitemaps.views.sitemap') ] diff --git a/docs/ref/urls.txt b/docs/ref/urls.txt index be09777b40..222f41a76e 100644 --- a/docs/ref/urls.txt +++ b/docs/ref/urls.txt @@ -78,7 +78,7 @@ The ``optional_dictionary`` and ``optional_name`` parameters are described in static() -------- -.. function:: static.static(prefix, view='django.views.static.serve', **kwargs) +.. function:: static.static(prefix, view=django.views.static.serve, **kwargs) Helper function to return a URL pattern for serving files in debug mode:: @@ -89,6 +89,11 @@ Helper function to return a URL pattern for serving files in debug mode:: # ... the rest of your URLconf goes here ... ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) +.. versionchanged:: 1.8 + + The ``view`` argument changed from a string + (``'django.views.static.serve'``) to the function. + url() ----- @@ -111,9 +116,14 @@ function or method. See :ref:`views-extra-options` for an example. See :ref:`Naming URL patterns ` for why the ``name`` parameter is useful. -The ``prefix`` parameter has the same meaning as the first argument to -``patterns()`` and is only relevant when you're passing a string as the -``view`` parameter. +.. deprecated:: 1.8 + + Support for string ``view`` arguments is deprecated and will be removed in + Django 2.0. Pass the callable instead. + + The ``prefix`` parameter has the same meaning as the first argument to + ``patterns()`` and is only relevant when you're passing a string as the + ``view`` parameter. include() --------- diff --git a/docs/releases/1.8.txt b/docs/releases/1.8.txt index e391b6de11..318b6ce49f 100644 --- a/docs/releases/1.8.txt +++ b/docs/releases/1.8.txt @@ -548,6 +548,13 @@ Updating your code is as simple as ensuring that ``urlpatterns`` is a list of url('^other/$', views.otherview), ] +Passing a string as ``view`` to :func:`~django.conf.urls.url` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Related to the previous item, referencing views as strings in the ``url()`` +function is deprecated. Pass the callable view as described in the previous +section instead. + ``django.test.SimpleTestCase.urls`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -580,8 +587,10 @@ for reversing instead. If you are using :mod:`django.contrib.sitemaps`, add the ``name`` argument to the ``url`` that references :func:`django.contrib.sitemaps.views.sitemap`:: - url(r'^sitemap\.xml$', 'django.contrib.sitemaps.views.sitemap', - {'sitemaps': sitemaps}, name='django.contrib.sitemaps.views.sitemap') + from django.contrib.sitemaps.views import sitemap + + url(r'^sitemap\.xml$', sitemap, {'sitemaps': sitemaps}, + name='django.contrib.sitemaps.views.sitemap') to ensure compatibility when reversing by Python path is removed in Django 2.0. diff --git a/docs/topics/http/urls.txt b/docs/topics/http/urls.txt index 837aaa446c..9c808d38b0 100644 --- a/docs/topics/http/urls.txt +++ b/docs/topics/http/urls.txt @@ -74,11 +74,13 @@ Here's a sample URLconf:: from django.conf.urls import url + from . import views + urlpatterns = [ - url(r'^articles/2003/$', 'news.views.special_case_2003'), - url(r'^articles/([0-9]{4})/$', 'news.views.year_archive'), - url(r'^articles/([0-9]{4})/([0-9]{2})/$', 'news.views.month_archive'), - url(r'^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$', 'news.views.article_detail'), + url(r'^articles/2003/$', views.special_case_2003), + url(r'^articles/([0-9]{4})/$', views.year_archive), + url(r'^articles/([0-9]{4})/([0-9]{2})/$', views.month_archive), + url(r'^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$', views.article_detail), ] Notes: @@ -96,7 +98,7 @@ Example requests: * A request to ``/articles/2005/03/`` would match the third entry in the list. Django would call the function - ``news.views.month_archive(request, '2005', '03')``. + ``views.month_archive(request, '2005', '03')``. * ``/articles/2005/3/`` would not match any URL patterns, because the third entry in the list requires two digits for the month. @@ -110,7 +112,7 @@ Example requests: pattern requires that the URL end with a slash. * ``/articles/2003/03/03/`` would match the final pattern. Django would call - the function ``news.views.article_detail(request, '2003', '03', '03')``. + the function ``views.article_detail(request, '2003', '03', '03')``. .. _Dive Into Python's explanation: http://www.diveintopython.net/regular_expressions/street_addresses.html#re.matching.2.3 @@ -131,11 +133,13 @@ Here's the above example URLconf, rewritten to use named groups:: from django.conf.urls import url + from . import views + urlpatterns = [ - url(r'^articles/2003/$', 'news.views.special_case_2003'), - url(r'^articles/(?P[0-9]{4})/$', 'news.views.year_archive'), - url(r'^articles/(?P[0-9]{4})/(?P[0-9]{2})/$', 'news.views.month_archive'), - url(r'^articles/(?P[0-9]{4})/(?P[0-9]{2})/(?P[0-9]{2})/$', 'news.views.article_detail'), + url(r'^articles/2003/$', views.special_case_2003), + url(r'^articles/(?P[0-9]{4})/$', views.year_archive), + url(r'^articles/(?P[0-9]{4})/(?P[0-9]{2})/$', views.month_archive), + url(r'^articles/(?P[0-9]{4})/(?P[0-9]{2})/(?P[0-9]{2})/$', views.article_detail), ] This accomplishes exactly the same thing as the previous example, with one @@ -143,11 +147,11 @@ subtle difference: The captured values are passed to view functions as keyword arguments rather than positional arguments. For example: * A request to ``/articles/2005/03/`` would call the function - ``news.views.month_archive(request, year='2005', month='03')``, instead - of ``news.views.month_archive(request, '2005', '03')``. + ``views.month_archive(request, year='2005', month='03')``, instead + of ``views.month_archive(request, '2005', '03')``. * A request to ``/articles/2003/03/03/`` would call the function - ``news.views.article_detail(request, year='2003', month='03', day='03')``. + ``views.article_detail(request, year='2003', month='03', day='03')``. In practice, this means your URLconfs are slightly more explicit and less prone to argument-order bugs -- and you can reorder the arguments in your views' @@ -191,9 +195,9 @@ Each captured argument is sent to the view as a plain Python string, regardless of what sort of match the regular expression makes. For example, in this URLconf line:: - url(r'^articles/(?P[0-9]{4})/$', 'news.views.year_archive'), + url(r'^articles/(?P[0-9]{4})/$', views.year_archive), -...the ``year`` argument to ``news.views.year_archive()`` will be a string, not +...the ``year`` argument to ``views.year_archive()`` will be a string, not an integer, even though the ``[0-9]{4}`` will only match integer strings. Specifying defaults for view arguments @@ -205,9 +209,11 @@ Here's an example URLconf and view:: # URLconf from django.conf.urls import url + from . import views + urlpatterns = [ - url(r'^blog/$', 'blog.views.page'), - url(r'^blog/page(?P[0-9]+)/$', 'blog.views.page'), + url(r'^blog/$', views.page), + url(r'^blog/page(?P[0-9]+)/$', views.page), ] # View (in blog/views.py) @@ -216,7 +222,7 @@ Here's an example URLconf and view:: ... In the above example, both URL patterns point to the same view -- -``blog.views.page`` -- but the first pattern doesn't capture anything from the +``views.page`` -- but the first pattern doesn't capture anything from the URL. If the first pattern matches, the ``page()`` function will use its default argument for ``num``, ``"1"``. If the second pattern matches, ``page()`` will use whatever ``num`` value was captured by the regex. @@ -290,13 +296,16 @@ Another possibility is to include additional URL patterns by using a list of from django.conf.urls import include, url + from apps.main import views as main_views + from credit import views as credit_views + extra_patterns = [ - url(r'^reports/(?P[0-9]+)/$', 'credit.views.report'), - url(r'^charge/$', 'credit.views.charge'), + url(r'^reports/(?P[0-9]+)/$', credit_views.report), + url(r'^charge/$', credit_views.charge), ] urlpatterns = [ - url(r'^$', 'apps.main.views.homepage'), + url(r'^$', main_views.homepage), url(r'^help/', include('apps.help.urls')), url(r'^credit/', include(extra_patterns)), ] @@ -381,7 +390,7 @@ For example:: ] In this example, for a request to ``/blog/2005/``, Django will call -``blog.views.year_archive(request, year='2005', foo='bar')``. +``views.year_archive(request, year='2005', foo='bar')``. This technique is used in the :doc:`syndication framework ` to pass metadata and @@ -444,60 +453,6 @@ URLconf, regardless of whether the line's view actually accepts those options as valid. For this reason, this technique is only useful if you're certain that every view in the included URLconf accepts the extra options you're passing. -Passing callable objects instead of strings -=========================================== - -Some developers find it more natural to pass the actual Python function object -rather than a string containing the path to its module. This alternative is -supported -- you can pass any callable object as the view. - -For example, given this URLconf in "string" notation:: - - from django.conf.urls import url - - urlpatterns = [ - url(r'^archive/$', 'mysite.views.archive'), - url(r'^about/$', 'mysite.views.about'), - url(r'^contact/$', 'mysite.views.contact'), - ] - -You can accomplish the same thing by passing objects rather than strings. Just -be sure to import the objects:: - - from django.conf.urls import url - from mysite.views import archive, about, contact - - urlpatterns = [ - url(r'^archive/$', archive), - url(r'^about/$', about), - url(r'^contact/$', contact), - ] - -The following example is functionally identical. It's just a bit more compact -because it imports the module that contains the views, rather than importing -each view individually:: - - from django.conf.urls import url - from mysite import views - - urlpatterns = [ - url(r'^archive/$', views.archive), - url(r'^about/$', views.about), - url(r'^contact/$', views.contact), - ] - -The style you use is up to you. - -Note that :doc:`class based views` must be -imported:: - - from django.conf.urls import url - from mysite.views import ClassBasedView - - urlpatterns = [ - url(r'^myview/$', ClassBasedView.as_view()), - ] - Reverse resolution of URLs ========================== @@ -553,9 +508,11 @@ Consider again this URLconf entry:: from django.conf.urls import url + from . import views + urlpatterns = [ #... - url(r'^articles/([0-9]{4})/$', 'news.views.year_archive', name='news-year-archive'), + url(r'^articles/([0-9]{4})/$', views.year_archive, name='news-year-archive'), #... ] @@ -756,9 +713,11 @@ For example:: from django.conf.urls import include, url + from app.helps import views + help_patterns = [ - url(r'^basic/$', 'apps.help.views.views.basic'), - url(r'^advanced/$', 'apps.help.views.views.advanced'), + url(r'^basic/$', views.basic), + url(r'^advanced/$', views.advanced), ] url(r'^help/', include((help_patterns, 'bar', 'foo'))), diff --git a/docs/topics/i18n/translation.txt b/docs/topics/i18n/translation.txt index fefabb535b..aeded28ad9 100644 --- a/docs/topics/i18n/translation.txt +++ b/docs/topics/i18n/translation.txt @@ -1100,22 +1100,25 @@ prepend the current active language code to all url patterns defined within from django.conf.urls import include, url from django.conf.urls.i18n import i18n_patterns + from about import views as about_views + from news import views as news_views + from sitemap.views imort sitemap + urlpatterns = [ - url(r'^sitemap\.xml$', 'sitemap.view', name='sitemap_xml'), + url(r'^sitemap\.xml$', sitemap, name='sitemap_xml'), ] news_patterns = [ - url(r'^$', 'news.views.index', name='index'), - url(r'^category/(?P[\w-]+)/$', 'news.views.category', name='category'), - url(r'^(?P[\w-]+)/$', 'news.views.details', name='detail'), + url(r'^$', news_views.index, name='index'), + url(r'^category/(?P[\w-]+)/$', news_views.category, name='category'), + url(r'^(?P[\w-]+)/$', news_views.details, name='detail'), ] urlpatterns += i18n_patterns( - url(r'^about/$', 'about.view', name='about'), + url(r'^about/$', about_views.main, name='about'), url(r'^news/', include(news_patterns, namespace='news')), ) - 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:: @@ -1155,22 +1158,25 @@ URL patterns can also be marked translatable using the from django.conf.urls.i18n import i18n_patterns from django.utils.translation import ugettext_lazy as _ + from about import views as about_views + from news import views as news_views + from sitemaps.views import sitemap + urlpatterns = [ - url(r'^sitemap\.xml$', 'sitemap.view', name='sitemap_xml'), + url(r'^sitemap\.xml$', sitemap, name='sitemap_xml'), ] news_patterns = [ - url(r'^$', 'news.views.index', name='index'), - url(_(r'^category/(?P[\w-]+)/$'), 'news.views.category', name='category'), - url(r'^(?P[\w-]+)/$', 'news.views.details', name='detail'), + url(r'^$', news_views.index, name='index'), + url(_(r'^category/(?P[\w-]+)/$'), news_views.category, name='category'), + url(r'^(?P[\w-]+)/$', news_views.details, name='detail'), ] urlpatterns += i18n_patterns( - url(_(r'^about/$'), 'about.view', name='about'), + url(_(r'^about/$'), about_views.main, name='about'), 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:: diff --git a/tests/admin_scripts/urls.py b/tests/admin_scripts/urls.py index ab17990322..b3f4eef15b 100644 --- a/tests/admin_scripts/urls.py +++ b/tests/admin_scripts/urls.py @@ -1,10 +1,11 @@ import os from django.conf.urls import url from django.utils._os import upath +from django.views.static import serve here = os.path.dirname(upath(__file__)) urlpatterns = [ - url(r'^custom_templates/(?P.*)$', 'django.views.static.serve', { + url(r'^custom_templates/(?P.*)$', serve, { 'document_root': os.path.join(here, 'custom_templates')}), ] diff --git a/tests/generic_views/urls.py b/tests/generic_views/urls.py index 84239159d1..a14eb6a9f4 100644 --- a/tests/generic_views/urls.py +++ b/tests/generic_views/urls.py @@ -1,4 +1,5 @@ from django.conf.urls import url +from django.contrib.auth import views as auth_views from django.views.decorators.cache import cache_page from django.views.generic import TemplateView @@ -257,5 +258,5 @@ urlpatterns = [ views.BookSigningDetail.as_view()), # Useful for testing redirects - url(r'^accounts/login/$', 'django.contrib.auth.views.login') + url(r'^accounts/login/$', auth_views.login) ] diff --git a/tests/middleware/extra_urls.py b/tests/middleware/extra_urls.py index 80f07c5074..20ded19c62 100644 --- a/tests/middleware/extra_urls.py +++ b/tests/middleware/extra_urls.py @@ -1,7 +1,9 @@ from django.conf.urls import url +from . import views + urlpatterns = [ - url(r'^customurlconf/noslash$', 'middleware.views.empty_view'), - url(r'^customurlconf/slash/$', 'middleware.views.empty_view'), - url(r'^customurlconf/needsquoting#/$', 'middleware.views.empty_view'), + url(r'^customurlconf/noslash$', views.empty_view), + url(r'^customurlconf/slash/$', views.empty_view), + url(r'^customurlconf/needsquoting#/$', views.empty_view), ] diff --git a/tests/middleware/urls.py b/tests/middleware/urls.py index 7d6689590c..8c6621d059 100644 --- a/tests/middleware/urls.py +++ b/tests/middleware/urls.py @@ -1,7 +1,9 @@ from django.conf.urls import url +from . import views + urlpatterns = [ - url(r'^noslash$', 'middleware.views.empty_view'), - url(r'^slash/$', 'middleware.views.empty_view'), - url(r'^needsquoting#/$', 'middleware.views.empty_view'), + url(r'^noslash$', views.empty_view), + url(r'^slash/$', views.empty_view), + url(r'^needsquoting#/$', views.empty_view), ] diff --git a/tests/model_permalink/urls.py b/tests/model_permalink/urls.py index 8ad8a16d05..be973aada5 100644 --- a/tests/model_permalink/urls.py +++ b/tests/model_permalink/urls.py @@ -1,5 +1,7 @@ from django.conf.urls import url +from . import views + urlpatterns = [ - url(r'^guitarists/(\w{1,50})/$', 'model_permalink.views.empty_view', name='guitarist_detail'), + url(r'^guitarists/(\w{1,50})/$', views.empty_view, name='guitarist_detail'), ] diff --git a/tests/template_tests/urls.py b/tests/template_tests/urls.py index f1c8632c06..2ddb6e232c 100644 --- a/tests/template_tests/urls.py +++ b/tests/template_tests/urls.py @@ -15,5 +15,5 @@ urlpatterns = [ # Unicode strings are permitted everywhere. url(r'^Юникод/(\w+)/$', views.client2, name="метка_оператора"), - url(r'^Юникод/(?P\S+)/$', 'template_tests.views.client2', name="метка_оператора_2"), + url(r'^Юникод/(?P\S+)/$', views.client2, name="метка_оператора_2"), ] diff --git a/tests/test_client/urls.py b/tests/test_client/urls.py index d56e151b01..4b34bfb113 100644 --- a/tests/test_client/urls.py +++ b/tests/test_client/urls.py @@ -1,4 +1,5 @@ from django.conf.urls import url +from django.contrib.auth import views as auth_views from django.views.generic import RedirectView from . import views @@ -32,6 +33,6 @@ urlpatterns = [ url(r'^mass_mail_sending_view/$', views.mass_mail_sending_view), url(r'^django_project_redirect/$', views.django_project_redirect), - url(r'^accounts/login/$', 'django.contrib.auth.views.login', {'template_name': 'login.html'}), - url(r'^accounts/logout/$', 'django.contrib.auth.views.logout'), + url(r'^accounts/login/$', auth_views.login, {'template_name': 'login.html'}), + url(r'^accounts/logout/$', auth_views.logout), ] diff --git a/tests/urlpatterns_reverse/erroneous_urls.py b/tests/urlpatterns_reverse/erroneous_urls.py index 2fec86f6cc..b4d6132966 100644 --- a/tests/urlpatterns_reverse/erroneous_urls.py +++ b/tests/urlpatterns_reverse/erroneous_urls.py @@ -1,26 +1,27 @@ +import warnings + from django.conf.urls import url from . import views -urlpatterns = [ - # View has erroneous import - url(r'erroneous_inner/$', views.erroneous_view), - # Module has erroneous import - # Remove in Django 2.0 along with erroneous_views_module as this is only - # an issue with string in urlpatterns - url(r'erroneous_outer/$', 'urlpatterns_reverse.erroneous_views_module.erroneous_view'), - # Module is an unqualified string - url(r'erroneous_unqualified/$', 'unqualified_view'), - # View does not exist - # Remove in Django 2.0 along with erroneous_views_module as this is only - # an issue with string in urlpatterns - url(r'missing_inner/$', 'urlpatterns_reverse.views.missing_view'), - # View is not callable - # Remove in Django 2.0 along with erroneous_views_module as this is only - # an issue with string in urlpatterns - url(r'uncallable/$', 'urlpatterns_reverse.views.uncallable'), - # Module does not exist - url(r'missing_outer/$', 'urlpatterns_reverse.missing_module.missing_view'), - # Regex contains an error (refs #6170) - url(r'(regex_error/$', views.empty_view), -] +# Test deprecated behavior of passing strings as view to url(). +# Some of these can be removed in Django 2.0 as they aren't convertable to +# callabls. +with warnings.catch_warnings(record=True): + warnings.filterwarnings('ignore', module='django.conf.urls') + urlpatterns = [ + # View has erroneous import + url(r'erroneous_inner/$', views.erroneous_view), + # Module has erroneous import + url(r'erroneous_outer/$', 'urlpatterns_reverse.erroneous_views_module.erroneous_view'), + # Module is an unqualified string + url(r'erroneous_unqualified/$', 'unqualified_view'), + # View does not exist + url(r'missing_inner/$', 'urlpatterns_reverse.views.missing_view'), + # View is not callable + url(r'uncallable/$', 'urlpatterns_reverse.views.uncallable'), + # Module does not exist + url(r'missing_outer/$', 'urlpatterns_reverse.missing_module.missing_view'), + # Regex contains an error (refs #6170) + url(r'(regex_error/$', views.empty_view), + ] diff --git a/tests/urlpatterns_reverse/namespace_urls.py b/tests/urlpatterns_reverse/namespace_urls.py index 166189d286..12cf85f7d3 100644 --- a/tests/urlpatterns_reverse/namespace_urls.py +++ b/tests/urlpatterns_reverse/namespace_urls.py @@ -10,9 +10,9 @@ class URLObject(object): def urls(self): return ([ - url(r'^inner/$', 'urlpatterns_reverse.views.empty_view', name='urlobject-view'), - url(r'^inner/(?P[0-9]+)/(?P[0-9]+)/$', 'urlpatterns_reverse.views.empty_view', name='urlobject-view'), - url(r'^inner/\+\\\$\*/$', 'urlpatterns_reverse.views.empty_view', name='urlobject-special-view'), + url(r'^inner/$', views.empty_view, name='urlobject-view'), + url(r'^inner/(?P[0-9]+)/(?P[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) diff --git a/tests/urlpatterns_reverse/urls.py b/tests/urlpatterns_reverse/urls.py index bc30280da0..943f92e956 100644 --- a/tests/urlpatterns_reverse/urls.py +++ b/tests/urlpatterns_reverse/urls.py @@ -2,12 +2,15 @@ import warnings from django.conf.urls import patterns, url, include -from .views import empty_view, empty_view_partial, empty_view_wrapped, absolute_kwargs_view +from .views import ( + absolute_kwargs_view, defaults_view, empty_view, empty_view_partial, + empty_view_wrapped, nested_view, +) other_patterns = [ url(r'non_path_include/$', empty_view, name='non_path_include'), - url(r'nested_path/$', 'urlpatterns_reverse.views.nested_view'), + url(r'nested_path/$', nested_view), ] # test deprecated patterns() function. convert to list of urls() in Django 2.0 @@ -68,8 +71,8 @@ with warnings.catch_warnings(record=True): url(r'absolute_arg_view/$', absolute_kwargs_view), # Tests for #13154. Mixed syntax to test both ways of defining URLs. - url(r'defaults_view1/(?P[0-9]+)/', 'urlpatterns_reverse.views.defaults_view', {'arg2': 1}, name='defaults'), - (r'defaults_view2/(?P[0-9]+)/', 'urlpatterns_reverse.views.defaults_view', {'arg2': 2}, 'defaults'), + url(r'defaults_view1/(?P[0-9]+)/', defaults_view, {'arg2': 1}, name='defaults'), + (r'defaults_view2/(?P[0-9]+)/', defaults_view, {'arg2': 2}, 'defaults'), url('^includes/', include(other_patterns)), ) diff --git a/tests/view_tests/generic_urls.py b/tests/view_tests/generic_urls.py index 6d44468ce6..1150172ab9 100644 --- a/tests/view_tests/generic_urls.py +++ b/tests/view_tests/generic_urls.py @@ -2,6 +2,7 @@ from __future__ import unicode_literals from django.conf.urls import url +from django.contrib.auth import views as auth_views from django.views.generic import RedirectView from .models import Article, DateArticle @@ -27,12 +28,12 @@ numeric_days_info_dict = dict(date_based_info_dict, day_format='%d') date_based_datefield_info_dict = dict(date_based_info_dict, queryset=DateArticle.objects.all()) urlpatterns = [ - url(r'^accounts/login/$', 'django.contrib.auth.views.login', {'template_name': 'login.html'}), - url(r'^accounts/logout/$', 'django.contrib.auth.views.logout'), + url(r'^accounts/login/$', auth_views.login, {'template_name': 'login.html'}), + url(r'^accounts/logout/$', auth_views.logout), # Special URLs for particular regression cases. - url('^中文/$', 'view_tests.views.redirect'), - url('^中文/target/$', 'view_tests.views.index_page'), + url('^中文/$', views.redirect), + url('^中文/target/$', views.index_page), ] # redirects, both temporary and permanent, with non-ASCII targets diff --git a/tests/view_tests/regression_21530_urls.py b/tests/view_tests/regression_21530_urls.py index 7a91ddebd7..706a08c888 100644 --- a/tests/view_tests/regression_21530_urls.py +++ b/tests/view_tests/regression_21530_urls.py @@ -1,5 +1,7 @@ from django.conf.urls import url +from . import views + urlpatterns = [ - url(r'^index/$', 'view_tests.views.index_page', name='index'), + url(r'^index/$', views.index_page, name='index'), ] diff --git a/tests/view_tests/urls.py b/tests/view_tests/urls.py index 202f8589eb..0dfc2d4446 100644 --- a/tests/view_tests/urls.py +++ b/tests/view_tests/urls.py @@ -3,7 +3,7 @@ from os import path from django.conf.urls import url, include from django.utils._os import upath -from django.views import defaults, i18n +from django.views import defaults, i18n, static from . import views @@ -71,7 +71,7 @@ urlpatterns = [ url(r'^jsi18n_template/$', views.jsi18n), # Static views - url(r'^site_media/(?P.*)$', 'django.views.static.serve', {'document_root': media_dir}), + url(r'^site_media/(?P.*)$', static.serve, {'document_root': media_dir}), ] urlpatterns += [