diff options
| author | Sjoerd Job Postmus <sjoerdjob@sjec.nl> | 2016-10-20 19:29:04 +0200 |
|---|---|---|
| committer | Tim Graham <timograham@gmail.com> | 2017-09-20 18:04:42 -0400 |
| commit | df41b5a05d4e00e80e73afe629072e37873e767a (patch) | |
| tree | baaf71ae695e2d3af604ea0d663284cb406c71e4 /docs/topics | |
| parent | c4c128d67c7dc2830631c6859a204c9d259f1fb1 (diff) | |
Fixed #28593 -- Added a simplified URL routing syntax per DEP 0201.
Thanks Aymeric Augustin for shepherding the DEP and patch review.
Thanks Marten Kenbeek and Tim Graham for contributing to the code.
Thanks Tom Christie, Shai Berger, and Tim Graham for the docs.
Diffstat (limited to 'docs/topics')
| -rw-r--r-- | docs/topics/auth/default.txt | 12 | ||||
| -rw-r--r-- | docs/topics/cache.txt | 6 | ||||
| -rw-r--r-- | docs/topics/class-based-views/generic-display.txt | 14 | ||||
| -rw-r--r-- | docs/topics/class-based-views/generic-editing.txt | 8 | ||||
| -rw-r--r-- | docs/topics/class-based-views/index.txt | 12 | ||||
| -rw-r--r-- | docs/topics/class-based-views/intro.txt | 10 | ||||
| -rw-r--r-- | docs/topics/class-based-views/mixins.txt | 4 | ||||
| -rw-r--r-- | docs/topics/http/urls.txt | 434 | ||||
| -rw-r--r-- | docs/topics/i18n/translation.txt | 52 |
9 files changed, 298 insertions, 254 deletions
diff --git a/docs/topics/auth/default.txt b/docs/topics/auth/default.txt index 4d8fbc37c9..85398f428f 100644 --- a/docs/topics/auth/default.txt +++ b/docs/topics/auth/default.txt @@ -502,7 +502,7 @@ The ``login_required`` decorator from django.contrib.auth import views as auth_views - url(r'^accounts/login/$', auth_views.LoginView.as_view()), + path('accounts/login/', auth_views.LoginView.as_view()), The :setting:`settings.LOGIN_URL <LOGIN_URL>` also accepts view function names and :ref:`named URL patterns <naming-url-patterns>`. This allows you @@ -896,7 +896,7 @@ easiest way is to include the provided URLconf in ``django.contrib.auth.urls`` in your own URLconf, for example:: urlpatterns = [ - url('^', include('django.contrib.auth.urls')), + path('', include('django.contrib.auth.urls')), ] This will include the following URL patterns:: @@ -919,7 +919,7 @@ your URLconf:: from django.contrib.auth import views as auth_views urlpatterns = [ - url('^change-password/$', auth_views.PasswordChangeView.as_view()), + path('change-password/', auth_views.PasswordChangeView.as_view()), ] The views have optional arguments you can use to alter the behavior of the @@ -928,8 +928,8 @@ provide the ``template_name`` argument. A way to do this is to provide keyword arguments in the URLconf, these will be passed on to the view. For example:: urlpatterns = [ - url( - '^change-password/$', + path( + 'change-password/', auth_views.PasswordChangeView.as_view(template_name='change-password.html'), ), ] @@ -1035,7 +1035,7 @@ implementation details see :ref:`using-the-views`. the ``as_view`` method in your URLconf. For example, this URLconf line would use :file:`myapp/login.html` instead:: - url(r'^accounts/login/$', auth_views.LoginView.as_view(template_name='myapp/login.html')), + path('accounts/login/', auth_views.LoginView.as_view(template_name='myapp/login.html')), You can also specify the name of the ``GET`` field which contains the URL to redirect to after login using ``redirect_field_name``. By default, the diff --git a/docs/topics/cache.txt b/docs/topics/cache.txt index d2aa8f98ec..6e6564d3e2 100644 --- a/docs/topics/cache.txt +++ b/docs/topics/cache.txt @@ -591,7 +591,7 @@ multiple URLs point at the same view, each URL will be cached separately. Continuing the ``my_view`` example, if your URLconf looks like this:: urlpatterns = [ - url(r'^foo/([0-9]{1,2})/$', my_view), + path('foo/<int:code>/', my_view), ] then requests to ``/foo/1/`` and ``/foo/23/`` will be cached separately, as @@ -637,7 +637,7 @@ Doing so is easy: simply wrap the view function with ``cache_page`` when you refer to it in the URLconf. Here's the old URLconf from earlier:: urlpatterns = [ - url(r'^foo/([0-9]{1,2})/$', my_view), + path('foo/<int:code>/', my_view), ] Here's the same thing, with ``my_view`` wrapped in ``cache_page``:: @@ -645,7 +645,7 @@ Here's the same thing, with ``my_view`` wrapped in ``cache_page``:: from django.views.decorators.cache import cache_page urlpatterns = [ - url(r'^foo/([0-9]{1,2})/$', cache_page(60 * 15)(my_view)), + path('foo/<int:code>/', cache_page(60 * 15)(my_view)), ] .. templatetag:: cache diff --git a/docs/topics/class-based-views/generic-display.txt b/docs/topics/class-based-views/generic-display.txt index 456c953cb1..2f41b0b500 100644 --- a/docs/topics/class-based-views/generic-display.txt +++ b/docs/topics/class-based-views/generic-display.txt @@ -117,11 +117,11 @@ Now we need to define a view:: Finally hook that view into your urls:: # urls.py - from django.conf.urls import url + from django.urls import path from books.views import PublisherList urlpatterns = [ - url(r'^publishers/$', PublisherList.as_view()), + path('publishers/', PublisherList.as_view()), ] That's all the Python code we need to write. We still need to write a template, @@ -332,11 +332,11 @@ various useful things are stored on ``self``; as well as the request Here, we have a URLconf with a single captured group:: # urls.py - from django.conf.urls import url + from django.urls import path from books.views import PublisherBookList urlpatterns = [ - url(r'^books/([\w-]+)/$', PublisherBookList.as_view()), + path('books/<publisher>/', PublisherBookList.as_view()), ] Next, we'll write the ``PublisherBookList`` view itself:: @@ -351,7 +351,7 @@ Next, we'll write the ``PublisherBookList`` view itself:: template_name = 'books/books_by_publisher.html' def get_queryset(self): - self.publisher = get_object_or_404(Publisher, name=self.args[0]) + self.publisher = get_object_or_404(Publisher, name=self.kwargs['publisher']) return Book.objects.filter(publisher=self.publisher) As you can see, it's quite easy to add more logic to the queryset selection; @@ -398,12 +398,12 @@ updated. First, we'd need to add an author detail bit in the URLconf to point to a custom view:: - from django.conf.urls import url + from django.urls import path from books.views import AuthorDetailView urlpatterns = [ #... - url(r'^authors/(?P<pk>[0-9]+)/$', AuthorDetailView.as_view(), name='author-detail'), + path('authors/<int:pk>/', AuthorDetailView.as_view(), name='author-detail'), ] Then we'd write our new view -- ``get_object`` is the method that retrieves the diff --git a/docs/topics/class-based-views/generic-editing.txt b/docs/topics/class-based-views/generic-editing.txt index 3fde51253a..69f2158021 100644 --- a/docs/topics/class-based-views/generic-editing.txt +++ b/docs/topics/class-based-views/generic-editing.txt @@ -149,14 +149,14 @@ Finally, we hook these new views into the URLconf: .. snippet:: :filename: urls.py - from django.conf.urls import url + from django.urls import path from myapp.views import AuthorCreate, AuthorUpdate, AuthorDelete urlpatterns = [ # ... - url(r'author/add/$', AuthorCreate.as_view(), name='author-add'), - url(r'author/(?P<pk>[0-9]+)/$', AuthorUpdate.as_view(), name='author-update'), - url(r'author/(?P<pk>[0-9]+)/delete/$', AuthorDelete.as_view(), name='author-delete'), + path('author/add/', AuthorCreate.as_view(), name='author-add'), + path('author/<int:pk>/', AuthorUpdate.as_view(), name='author-update'), + path('author/<int:pk>/delete/', AuthorDelete.as_view(), name='author-delete'), ] .. note:: diff --git a/docs/topics/class-based-views/index.txt b/docs/topics/class-based-views/index.txt index 91dd6ff944..6a629cb887 100644 --- a/docs/topics/class-based-views/index.txt +++ b/docs/topics/class-based-views/index.txt @@ -38,11 +38,11 @@ URLconf. If you're only changing a few simple attributes on a class-based view, you can simply pass them into the :meth:`~django.views.generic.base.View.as_view` method call itself:: - from django.conf.urls import url + from django.urls import path from django.views.generic import TemplateView urlpatterns = [ - url(r'^about/$', TemplateView.as_view(template_name="about.html")), + path('about/', TemplateView.as_view(template_name="about.html")), ] Any arguments passed to :meth:`~django.views.generic.base.View.as_view` will @@ -75,11 +75,11 @@ class method instead, which provides a function-like entry to class-based views:: # urls.py - from django.conf.urls import url + from django.urls import path from some_app.views import AboutView urlpatterns = [ - url(r'^about/$', AboutView.as_view()), + path('about/', AboutView.as_view()), ] @@ -100,11 +100,11 @@ preferable to ask the API when the most recent book was published. We map the URL to book list view in the URLconf:: - from django.conf.urls import url + from django.urls import path from books.views import BookListView urlpatterns = [ - url(r'^books/$', BookListView.as_view()), + path('books/', BookListView.as_view()), ] And the view:: diff --git a/docs/topics/class-based-views/intro.txt b/docs/topics/class-based-views/intro.txt index 32562a8744..f4a5f5ac24 100644 --- a/docs/topics/class-based-views/intro.txt +++ b/docs/topics/class-based-views/intro.txt @@ -89,11 +89,11 @@ request to a matching method if one is defined, or raises :class:`~django.http.HttpResponseNotAllowed` if not:: # urls.py - from django.conf.urls import url + from django.urls import path from myapp.views import MyView urlpatterns = [ - url(r'^about/$', MyView.as_view()), + path('about/', MyView.as_view()), ] @@ -130,7 +130,7 @@ Another option is to configure class attributes as keyword arguments to the :meth:`~django.views.generic.base.View.as_view` call in the URLconf:: urlpatterns = [ - url(r'^about/$', GreetingView.as_view(greeting="G'day")), + path('about/', GreetingView.as_view(greeting="G'day")), ] .. note:: @@ -245,8 +245,8 @@ The easiest place to do this is in the URLconf where you deploy your view:: from .views import VoteView urlpatterns = [ - url(r'^about/$', login_required(TemplateView.as_view(template_name="secret.html"))), - url(r'^vote/$', permission_required('polls.can_vote')(VoteView.as_view())), + path('about/', login_required(TemplateView.as_view(template_name="secret.html"))), + path('vote/', permission_required('polls.can_vote')(VoteView.as_view())), ] This approach applies the decorator on a per-instance basis. If you diff --git a/docs/topics/class-based-views/mixins.txt b/docs/topics/class-based-views/mixins.txt index 8da0834866..4a47af6f11 100644 --- a/docs/topics/class-based-views/mixins.txt +++ b/docs/topics/class-based-views/mixins.txt @@ -258,12 +258,12 @@ We can hook this into our URLs easily enough: .. snippet:: :filename: urls.py - from django.conf.urls import url + from django.urls import path from books.views import RecordInterest urlpatterns = [ #... - url(r'^author/(?P<pk>[0-9]+)/interest/$', RecordInterest.as_view(), name='author-interest'), + path('author/<int:pk>/interest/', RecordInterest.as_view(), name='author-interest'), ] Note the ``pk`` named group, which diff --git a/docs/topics/http/urls.txt b/docs/topics/http/urls.txt index d5bae7f072..449f421775 100644 --- a/docs/topics/http/urls.txt +++ b/docs/topics/http/urls.txt @@ -19,8 +19,7 @@ Overview To design URLs for an app, you create a Python module informally called a **URLconf** (URL configuration). This module is pure Python code and is a -simple mapping between URL patterns (simple regular expressions) to Python -functions (your views). +mapping between URL path expressions to Python functions (your views). This mapping can be as short or as long as needed. It can reference other mappings. And, because it's pure Python code, it can be constructed @@ -45,25 +44,26 @@ algorithm the system follows to determine which Python code to execute: :setting:`ROOT_URLCONF` setting. 2. Django loads that Python module and looks for the variable - ``urlpatterns``. This should be a Python list of :func:`django.conf.urls.url` - instances. + ``urlpatterns``. This should be a Python list of :func:`django.urls.path` + and/or :func:`django.urls.re_path` instances. 3. Django runs through each URL pattern, in order, and stops at the first one that matches the requested URL. -4. Once one of the regexes matches, Django imports and calls the given view, - which is a simple Python function (or a :doc:`class-based view +4. Once one of the URL patterns matches, Django imports and calls the given + view, which is a simple Python function (or a :doc:`class-based view </topics/class-based-views/index>`). The view gets passed the following arguments: * An instance of :class:`~django.http.HttpRequest`. - * If the matched regular expression returned no named groups, then the + * If the matched URL pattern returned no named groups, then the matches from the regular expression are provided as positional arguments. - * The keyword arguments are made up of any named groups matched by the - regular expression, overridden by any arguments specified in the optional - ``kwargs`` argument to :func:`django.conf.urls.url`. + * The keyword arguments are made up of any named parts matched by the + path expression, overridden by any arguments specified in the optional + ``kwargs`` argument to :func:`django.urls.path` or + :func:`django.urls.re_path`. -5. If no regex matches, or if an exception is raised during any +5. If no URL pattern matches, or if an exception is raised during any point in this process, Django invokes an appropriate error-handling view. See `Error handling`_ below. @@ -72,36 +72,33 @@ Example Here's a sample URLconf:: - from django.conf.urls import url + from django.urls import path from . import views urlpatterns = [ - 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), + path('articles/2003/', views.special_case_2003), + path('articles/<int:year>/', views.year_archive), + path('articles/<int:year>/<int:month>/', views.month_archive), + path('articles/<int:year>/<int:month>/<slug>/', views.article_detail), ] Notes: -* To capture a value from the URL, just put parenthesis around it. +* To capture a value from the URL, use angle brackets. -* There's no need to add a leading slash, because every URL has that. For - example, it's ``^articles``, not ``^/articles``. +* Captured values can optionally include a converter type. For example, use + ``<int:name>`` to capture an integer parameter. If a converter isn't included, + any string, excluding a ``/`` character, is matched. -* The ``'r'`` in front of each regular expression string is optional but - recommended. It tells Python that a string is "raw" -- that nothing in - the string should be escaped. See `Dive Into Python's explanation`_. +* There's no need to add a leading slash, because every URL has that. For + example, it's ``articles``, not ``/articles``. Example requests: * A request to ``/articles/2005/03/`` would match the third entry in the list. Django would call the function - ``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. + ``views.month_archive(request, year=2005, month=3)``. * ``/articles/2003/`` would match the first pattern in the list, not the second one, because the patterns are tested in order, and the first one @@ -112,66 +109,163 @@ Example requests: * ``/articles/2003`` would not match any of these patterns, because each pattern requires that the URL end with a slash. -* ``/articles/2003/03/03/`` would match the final pattern. Django would call - the function ``views.article_detail(request, '2003', '03', '03')``. +* ``/articles/2003/03/building-a-django-site/`` would match the final + pattern. Django would call the function + ``views.article_detail(request, year=2003, month=3, slug="building-a-django-site")``. + +Path converters +=============== + +The following path converters are available by default: + +* ``str`` - Matches any non-empty string, excluding the path separator, ``'/'``. + This is the default if a converter isn't included in the expression. + +* ``int`` - Matches zero or any positive integer. Returns an `int`. + +* ``slug`` - Matches any slug string consisting of ASCII letters or numbers, + plus the hyphen and underscore characters. For example, + ``building-your-1st-django-site``. + +* ``uuid`` - Matches a formatted UUID. For example, + ``075194d3-6885-417e-a8a8-6c931e272f00``. Returns a :class:`~uuid.UUID` + instance. + +* ``path`` - Matches any non-empty string, including the path separator, + ``'/'``. This allows you to match against a complete URL path rather than + just a segment of a URL path as with ``str``. + +.. _registering-custom-path-converters: + +Registering custom path converters +================================== + +For more complex matching requirements, you can define your own path converters. + +A converter is a class that includes the following: + +* A ``regex`` class attribute, as a string. + +* A ``to_python(self, value)`` method, which handles converting the matched + string into the type that should be passed to the view function. It should + raise ``ValueError`` if it can't convert the given value. + +* A ``to_url(self, value)`` method, which handles converting the Python type + into a string to be used in the URL. + +For example:: + + class FourDigitYearConverter: + regex = '[0-9]{4}' + + def to_python(self, value): + return int(value) + + def to_url(self, value): + return '%04d' % value + +Register custom converter classes in your URLconf using +:func:`~django.urls.register_converter`:: + + from django.urls import register_converter, path -.. _Dive Into Python's explanation: http://www.diveintopython3.net/regular-expressions.html#streetaddresses + from . import converters, views -Named groups -============ + register_converter(converters.FourDigitYearConverter, 'yyyy') -The above example used simple, *non-named* regular-expression groups (via -parenthesis) to capture bits of the URL and pass them as *positional* arguments -to a view. In more advanced usage, it's possible to use *named* -regular-expression groups to capture URL bits and pass them as *keyword* -arguments to a view. + urlpatterns = [ + path('articles/2003/', views.special_case_2003), + path('articles/<yyyy:year>/', views.year_archive), + ... + ] -In Python regular expressions, the syntax for named regular-expression groups +Using regular expressions +========================= + +If the paths and converters syntax isn't sufficient for defining your URL +patterns, you can also use regular expressions. To do so, use +:func:`~django.urls.re_path` instead of :func:`~django.urls.path`. + +In Python regular expressions, the syntax for named regular expression groups is ``(?P<name>pattern)``, where ``name`` is the name of the group and ``pattern`` is some pattern to match. -Here's the above example URLconf, rewritten to use named groups:: +Here's the example URLconf from earlier, rewritten using regular expressions:: - from django.conf.urls import url + from django.urls import path, re_path from . import views urlpatterns = [ - url(r'^articles/2003/$', views.special_case_2003), - url(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive), - url(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.month_archive), - url(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<day>[0-9]{2})/$', views.article_detail), + path('articles/2003/', views.special_case_2003), + re_path('articles/(?P<year>[0-9]{4})/', views.year_archive), + re_path('articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/', views.month_archive), + re_path('articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<slug>[^/]+)/', views.article_detail), ] -This accomplishes exactly the same thing as the previous example, with one -subtle difference: The captured values are passed to view functions as keyword -arguments rather than positional arguments. For example: +This accomplishes roughly the same thing as the previous example, except: + +* The exact URLs that will match are slightly more constrained. For example, + the year 10000 will no longer match since the year integers are constrained + to be exactly four digits long. + +* Each captured argument is sent to the view as a string, regardless of what + sort of match the regular expression makes. + +When switching from using :func:`~django.urls.path` to +:func:`~django.urls.re_path` or vice versa, it's particularly important to be +aware that the type of the view arguments may change, and so you may need to +adapt your views. + +Using unnamed regular expression groups +--------------------------------------- + +As well as the named group syntax, e.g. ``(?P<year>[0-9]{4})``, you can +also use the shorter unnamed group, e.g. ``([0-9]{4})``. + +This usage isn't particularly recommended as it makes it easier to accidentally +introduce errors between the intended meaning of a match and the arguments +of the view. + +In either case, using only one style within an given regex is recommended. When +both styles are mixed, any unnamed groups are ignored and only named groups are +passed to the view function. -* A request to ``/articles/2005/03/`` would call the function - ``views.month_archive(request, year='2005', month='03')``, instead - of ``views.month_archive(request, '2005', '03')``. +Nested arguments +---------------- -* A request to ``/articles/2003/03/03/`` would call the function - ``views.article_detail(request, year='2003', month='03', day='03')``. +Regular expressions allow nested arguments, and Django will resolve them and +pass them to the view. When reversing, Django will try to fill in all outer +captured arguments, ignoring any nested captured arguments. Consider the +following URL patterns which optionally take a page argument:: -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' -function definitions. Of course, these benefits come at the cost of brevity; -some developers find the named-group syntax ugly and too verbose. + from django.urls import re_path -The matching/grouping algorithm -------------------------------- + urlpatterns = [ + re_path(r'blog/(page-(\d+)/)?$', blog_articles), # bad + re_path(r'comments/(?:page-(?P<page_number>\d+)/)?$', comments), # good + ] -Here's the algorithm the URLconf parser follows, with respect to named groups -vs. non-named groups in a regular expression: +Both patterns use nested arguments and will resolve: for example, +``blog/page-2/`` will result in a match to ``blog_articles`` with two +positional arguments: ``page-2/`` and ``2``. The second pattern for +``comments`` will match ``comments/page-2/`` with keyword argument +``page_number`` set to 2. The outer argument in this case is a non-capturing +argument ``(?:...)``. -1. If there are any named arguments, it will use those, ignoring non-named - arguments. +The ``blog_articles`` view needs the outermost captured argument to be reversed, +``page-2/`` or no arguments in this case, while ``comments`` can be reversed +with either no arguments or a value for ``page_number``. -2. Otherwise, it will pass all non-named arguments as positional arguments. +Nested captured arguments create a strong coupling between the view arguments +and the URL as illustrated by ``blog_articles``: the view receives part of the +URL (``page-2/``) instead of only the value the view is interested in. This +coupling is even more pronounced when reversing, since to reverse the view we +need to pass the piece of URL instead of the page number. -In both cases, any extra keyword arguments that have been given as per `Passing -extra options to view functions`_ (below) will also be passed to the view. +As a rule of thumb, only capture the values the view needs to work with and +use non-capturing arguments when the regular expression needs an argument but +the view ignores it. What the URLconf searches against ================================= @@ -189,18 +283,6 @@ The URLconf doesn't look at the request method. In other words, all request methods -- ``POST``, ``GET``, ``HEAD``, etc. -- will be routed to the same function for the same URL. -Captured arguments are always strings -===================================== - -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<year>[0-9]{4})/$', views.year_archive), - -...the ``year`` argument passed 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 ====================================== @@ -208,25 +290,25 @@ A convenient trick is to specify default parameters for your views' arguments. Here's an example URLconf and view:: # URLconf - from django.conf.urls import url + from django.urls import path from . import views urlpatterns = [ - url(r'^blog/$', views.page), - url(r'^blog/page(?P<num>[0-9]+)/$', views.page), + path('blog/', views.page), + path('blog/page<int:num>/', views.page), ] # View (in blog/views.py) - def page(request, num="1"): + def page(request, num=1): # Output the appropriate page of blog entries, according to num. ... In the above example, both URL patterns point to the same view -- ``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. +default argument for ``num``, ``1``. If the second pattern matches, +``page()`` will use whatever ``num`` value was captured. Performance =========== @@ -237,14 +319,14 @@ accessed. This makes the system blazingly fast. Syntax of the ``urlpatterns`` variable ====================================== -``urlpatterns`` should be a Python list of :func:`~django.conf.urls.url` -instances. +``urlpatterns`` should be a Python list of :func:`~django.urls.path` and/or +:func:`~django.urls.re_path` instances. Error handling ============== -When Django can't find a regex matching the requested URL, or when an -exception is raised, Django will invoke an error-handling view. +When Django can't find a match for the requested URL, or when an exception is +raised, Django invokes an error-handling view. The views to use for these cases are specified by four variables. Their default values should suffice for most projects, but further customization is @@ -277,39 +359,37 @@ essentially "roots" a set of URLs below other ones. For example, here's an excerpt of the URLconf for the `Django website`_ itself. It includes a number of other URLconfs:: - from django.conf.urls import include, url + from django.urls import include, path urlpatterns = [ # ... snip ... - url(r'^community/', include('django_website.aggregator.urls')), - url(r'^contact/', include('django_website.contact.urls')), + path('community/', include('aggregator.urls')), + path('contact/', include('contact.urls')), # ... snip ... ] -Note that the regular expressions in this example don't have a ``$`` -(end-of-string match character) but do include a trailing slash. Whenever -Django encounters ``include()`` (:func:`django.conf.urls.include()`), it chops -off whatever part of the URL matched up to that point and sends the remaining +Whenever Django encounters :func:`~django.urls.include()`, it chops off +whatever part of the URL matched up to that point and sends the remaining string to the included URLconf for further processing. Another possibility is to include additional URL patterns by using a list of -:func:`~django.conf.urls.url` instances. For example, consider this URLconf:: +:func:`~django.urls.path` instances. For example, consider this URLconf:: - from django.conf.urls import include, url + from django.urls import include, path from apps.main import views as main_views from credit import views as credit_views extra_patterns = [ - url(r'^reports/$', credit_views.report), - url(r'^reports/(?P<id>[0-9]+)/$', credit_views.report), - url(r'^charge/$', credit_views.charge), + path('reports/', credit_views.report), + path('reports/<int:id>/', credit_views.report), + path('charge/', credit_views.charge), ] urlpatterns = [ - url(r'^$', main_views.homepage), - url(r'^help/', include('apps.help.urls')), - url(r'^credit/', include(extra_patterns)), + path('', main_views.homepage), + path('help/', include('apps.help.urls')), + path('credit/', include(extra_patterns)), ] In this example, the ``/credit/reports/`` URL will be handled by the @@ -318,28 +398,28 @@ In this example, the ``/credit/reports/`` URL will be handled by the This can be used to remove redundancy from URLconfs where a single pattern prefix is used repeatedly. For example, consider this URLconf:: - from django.conf.urls import url + from django.urls import path from . import views urlpatterns = [ - url(r'^(?P<page_slug>[\w-]+)-(?P<page_id>\w+)/history/$', views.history), - url(r'^(?P<page_slug>[\w-]+)-(?P<page_id>\w+)/edit/$', views.edit), - url(r'^(?P<page_slug>[\w-]+)-(?P<page_id>\w+)/discuss/$', views.discuss), - url(r'^(?P<page_slug>[\w-]+)-(?P<page_id>\w+)/permissions/$', views.permissions), + path('<page_slug>-<page_id>/history/', views.history), + path('<page_slug>-<page_id>/edit/', views.edit), + path('<page_slug>-<page_id>/discuss/', views.discuss), + path('<page_slug>-<page_id>/permissions/', views.permissions), ] We can improve this by stating the common path prefix only once and grouping the suffixes that differ:: - from django.conf.urls import include, url + from django.urls import include, path from . import views urlpatterns = [ - url(r'^(?P<page_slug>[\w-]+)-(?P<page_id>\w+)/', include([ - url(r'^history/$', views.history), - url(r'^edit/$', views.edit), - url(r'^discuss/$', views.discuss), - url(r'^permissions/$', views.permissions), + path('<page_slug>-<page_id>/', include([ + path('history/', views.history), + path('edit/', views.edit), + path('discuss/', views.discuss), + path('permissions/', views.permissions), ])), ] @@ -352,60 +432,24 @@ An included URLconf receives any captured parameters from parent URLconfs, so the following example is valid:: # In settings/urls/main.py - from django.conf.urls import include, url + from django.urls import include, path urlpatterns = [ - url(r'^(?P<username>\w+)/blog/', include('foo.urls.blog')), + path('<username>/blog/', include('foo.urls.blog')), ] # In foo/urls/blog.py - from django.conf.urls import url + from django.urls import path from . import views urlpatterns = [ - url(r'^$', views.blog.index), - url(r'^archive/$', views.blog.archive), + path('', views.blog.index), + path('archive/', views.blog.archive), ] In the above example, the captured ``"username"`` variable is passed to the included URLconf, as expected. -Nested arguments -================ - -Regular expressions allow nested arguments, and Django will resolve them and -pass them to the view. When reversing, Django will try to fill in all outer -captured arguments, ignoring any nested captured arguments. Consider the -following URL patterns which optionally take a page argument:: - - from django.conf.urls import url - - urlpatterns = [ - url(r'blog/(page-(\d+)/)?$', blog_articles), # bad - url(r'comments/(?:page-(?P<page_number>\d+)/)?$', comments), # good - ] - -Both patterns use nested arguments and will resolve: for example, -``blog/page-2/`` will result in a match to ``blog_articles`` with two -positional arguments: ``page-2/`` and ``2``. The second pattern for -``comments`` will match ``comments/page-2/`` with keyword argument -``page_number`` set to 2. The outer argument in this case is a non-capturing -argument ``(?:...)``. - -The ``blog_articles`` view needs the outermost captured argument to be reversed, -``page-2/`` or no arguments in this case, while ``comments`` can be reversed -with either no arguments or a value for ``page_number``. - -Nested captured arguments create a strong coupling between the view arguments -and the URL as illustrated by ``blog_articles``: the view receives part of the -URL (``page-2/``) instead of only the value the view is interested in. This -coupling is even more pronounced when reversing, since to reverse the view we -need to pass the piece of URL instead of the page number. - -As a rule of thumb, only capture the values the view needs to work with and -use non-capturing arguments when the regular expression needs an argument but -the view ignores it. - .. _views-extra-options: Passing extra options to view functions @@ -414,21 +458,21 @@ Passing extra options to view functions URLconfs have a hook that lets you pass extra arguments to your view functions, as a Python dictionary. -The :func:`django.conf.urls.url` function can take an optional third argument +The :func:`~django.urls.path` function can take an optional third argument which should be a dictionary of extra keyword arguments to pass to the view function. For example:: - from django.conf.urls import url + from django.urls import path from . import views urlpatterns = [ - url(r'^blog/(?P<year>[0-9]{4})/$', views.year_archive, {'foo': 'bar'}), + path('blog/<int:year>/', views.year_archive, {'foo': 'bar'}), ] In this example, for a request to ``/blog/2005/``, Django will call -``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 </ref/contrib/syndication>` to pass metadata and @@ -444,46 +488,45 @@ options to views. Passing extra options to ``include()`` -------------------------------------- -Similarly, you can pass extra options to :func:`~django.conf.urls.include`. -When you pass extra options to ``include()``, *each* line in the included -URLconf will be passed the extra options. +Similarly, you can pass extra options to :func:`~django.urls.include` and +each line in the included URLconf will be passed the extra options. For example, these two URLconf sets are functionally identical: Set one:: # main.py - from django.conf.urls import include, url + from django.urls import include, path urlpatterns = [ - url(r'^blog/', include('inner'), {'blogid': 3}), + path('blog/', include('inner'), {'blog_id': 3}), ] # inner.py - from django.conf.urls import url + from django.urls import path from mysite import views urlpatterns = [ - url(r'^archive/$', views.archive), - url(r'^about/$', views.about), + path('archive/', views.archive), + path('about/', views.about), ] Set two:: # main.py - from django.conf.urls import include, url + from django.urls import include, path from mysite import views urlpatterns = [ - url(r'^blog/', include('inner')), + path('blog/', include('inner')), ] # inner.py - from django.conf.urls import url + from django.urls import path urlpatterns = [ - url(r'^archive/$', views.archive, {'blogid': 3}), - url(r'^about/$', views.about, {'blogid': 3}), + path('archive/', views.archive, {'blog_id': 3}), + path('about/', views.about, {'blog_id': 3}), ] Note that extra options will *always* be passed to *every* line in the included @@ -543,18 +586,18 @@ Examples Consider again this URLconf entry:: - from django.conf.urls import url + from django.urls import path from . import views urlpatterns = [ #... - url(r'^articles/([0-9]{4})/$', views.year_archive, name='news-year-archive'), + path('articles/<int:year>/', views.year_archive, name='news-year-archive'), #... ] According to this design, the URL for the archive corresponding to year *nnnn* -is ``/articles/nnnn/``. +is ``/articles/<nnnn>/``. You can obtain these in template code by using: @@ -720,24 +763,24 @@ displaying polls. .. snippet:: :filename: urls.py - from django.conf.urls import include, url + from django.urls import include, path urlpatterns = [ - url(r'^author-polls/', include('polls.urls', namespace='author-polls')), - url(r'^publisher-polls/', include('polls.urls', namespace='publisher-polls')), + path('author-polls/', include('polls.urls', namespace='author-polls')), + path('publisher-polls/', include('polls.urls', namespace='publisher-polls')), ] .. snippet:: :filename: polls/urls.py - from django.conf.urls import url + from django.urls import path 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'), + path('', views.IndexView.as_view(), name='index'), + path('<int:pk>/', views.DetailView.as_view(), name='detail'), ... ] @@ -783,60 +826,61 @@ Application namespaces of included URLconfs can be specified in two ways. 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. +module, or a string reference to the module, to :func:`~django.urls.include`, +not the list of ``urlpatterns`` itself. .. snippet:: :filename: polls/urls.py - from django.conf.urls import url + from django.urls import path 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'), + path('', views.IndexView.as_view(), name='index'), + path('<int:pk>/', views.DetailView.as_view(), name='detail'), ... ] .. snippet:: :filename: urls.py - from django.conf.urls import include, url + from django.urls import include, path urlpatterns = [ - url(r'^polls/', include('polls.urls')), + path('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 2-tuple containing:: +you ``include()`` a list of :func:`~django.urls.path` or +:func:`~django.urls.re_path` instances, the URLs contained in that object +will be added to the global namespace. However, you can also ``include()`` a +2-tuple containing:: - (<list of url() instances>, <application namespace>) + (<list of path()/re_path() instances>, <application namespace>) For example:: - from django.conf.urls import include, url + from django.urls import include, path from . import views polls_patterns = ([ - url(r'^$', views.IndexView.as_view(), name='index'), - url(r'^(?P<pk>\d+)/$', views.DetailView.as_view(), name='detail'), + path('', views.IndexView.as_view(), name='index'), + path('<int:pk>/', views.DetailView.as_view(), name='detail'), ], 'polls') urlpatterns = [ - url(r'^polls/', include(polls_patterns)), + path('polls/', include(polls_patterns)), ] This will include the nominated URL patterns into the given application namespace. The instance namespace can be specified using the ``namespace`` argument to -:func:`~django.conf.urls.include`. If the instance namespace is not specified, +:func:`~django.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. diff --git a/docs/topics/i18n/translation.txt b/docs/topics/i18n/translation.txt index 148028c717..dcecbf61a5 100644 --- a/docs/topics/i18n/translation.txt +++ b/docs/topics/i18n/translation.txt @@ -992,15 +992,15 @@ The ``JavaScriptCatalog`` view from django.views.i18n import JavaScriptCatalog urlpatterns = [ - url(r'^jsi18n/$', JavaScriptCatalog.as_view(), name='javascript-catalog'), + path('jsi18n/', JavaScriptCatalog.as_view(), name='javascript-catalog'), ] **Example with custom packages**:: urlpatterns = [ - url(r'^jsi18n/myapp/$', - JavaScriptCatalog.as_view(packages=['your.app.label']), - name='javascript-catalog'), + path('jsi18n/myapp/', + JavaScriptCatalog.as_view(packages=['your.app.label']), + name='javascript-catalog'), ] If your root URLconf uses :func:`~django.conf.urls.i18n.i18n_patterns`, @@ -1012,7 +1012,7 @@ The ``JavaScriptCatalog`` view from django.conf.urls.i18n import i18n_patterns urlpatterns = i18n_patterns( - url(r'^jsi18n/$', JavaScriptCatalog.as_view(), name='javascript-catalog'), + path('jsi18n/', JavaScriptCatalog.as_view(), name='javascript-catalog'), ) The precedence of translations is such that the packages appearing later in the @@ -1235,9 +1235,9 @@ URL:: # The value returned by get_version() must change when translations change. urlpatterns = [ - url(r'^jsi18n/$', - cache_page(86400, key_prefix='js18n-%s' % get_version())(JavaScriptCatalog.as_view()), - name='javascript-catalog'), + path('jsi18n/', + cache_page(86400, key_prefix='js18n-%s' % get_version())(JavaScriptCatalog.as_view()), + name='javascript-catalog'), ] Client-side caching will save bandwidth and make your site load faster. If @@ -1253,9 +1253,9 @@ whenever you restart your application server:: last_modified_date = timezone.now() urlpatterns = [ - url(r'^jsi18n/$', - last_modified(lambda req, **kw: last_modified_date)(JavaScriptCatalog.as_view()), - name='javascript-catalog'), + path('jsi18n/', + last_modified(lambda req, **kw: last_modified_date)(JavaScriptCatalog.as_view()), + name='javascript-catalog'), ] You can even pre-generate the JavaScript catalog as part of your deployment @@ -1302,26 +1302,26 @@ translations to existing site so that the current URLs won't change. Example URL patterns:: - from django.conf.urls import include, url from django.conf.urls.i18n import i18n_patterns + from django.urls import include, url from about import views as about_views from news import views as news_views from sitemap.views import sitemap urlpatterns = [ - url(r'^sitemap\.xml$', sitemap, name='sitemap-xml'), + path('sitemap.xml', sitemap, name='sitemap-xml'), ] 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'), + path('', news_views.index, name='index'), + path('category/<slug>/', news_views.category, name='category'), + path('<slug>/', news_views.details, name='detail'), ], 'news') urlpatterns += i18n_patterns( - url(r'^about/$', about_views.main, name='about'), - url(r'^news/', include(news_patterns, namespace='news')), + path('about/', about_views.main, name='about'), + path('news/', include(news_patterns, namespace='news')), ) After defining these URL patterns, Django will automatically add the @@ -1371,8 +1371,8 @@ Translating URL patterns URL patterns can also be marked translatable using the :func:`~django.utils.translation.gettext_lazy` function. Example:: - from django.conf.urls import include, url from django.conf.urls.i18n import i18n_patterns + from django.urls import include, path from django.utils.translation import gettext_lazy as _ from about import views as about_views @@ -1380,18 +1380,18 @@ URL patterns can also be marked translatable using the from sitemaps.views import sitemap urlpatterns = [ - url(r'^sitemap\.xml$', sitemap, name='sitemap-xml'), + path('sitemap.xml', sitemap, name='sitemap-xml'), ] 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'), + path('', news_views.index, name='index'), + path(_('category/<slug>/'), news_views.category, name='category'), + path('<slug>/', news_views.details, name='detail'), ], 'news') urlpatterns += i18n_patterns( - url(_(r'^about/$'), about_views.main, name='about'), - url(_(r'^news/'), include(news_patterns, namespace='news')), + path(_('about/'), about_views.main, name='about'), + path(_('news/'), include(news_patterns, namespace='news')), ) After you've created the translations, the :func:`~django.urls.reverse` @@ -1750,7 +1750,7 @@ back to the previous page. Activate this view by adding the following line to your URLconf:: - url(r'^i18n/', include('django.conf.urls.i18n')), + path('i18n/', include('django.conf.urls.i18n')), (Note that this example makes the view available at ``/i18n/setlang/``.) |
