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.
This commit is contained in:
Sjoerd Job Postmus 2016-10-20 19:29:04 +02:00 committed by Tim Graham
parent c4c128d67c
commit df41b5a05d
77 changed files with 1663 additions and 1105 deletions

View file

@ -191,31 +191,30 @@ example above:
.. snippet::
:filename: mysite/news/urls.py
from django.conf.urls import url
from django.urls import path
from . import views
urlpatterns = [
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/<int:year>/', views.year_archive),
path('articles/<int:year>/<int:month>/', views.month_archive),
path('articles/<int:year>/<int:month>/<int:pk>/', views.article_detail),
]
The code above maps URLs, as simple :ref:`regular expressions <regex-howto>`,
to the location of Python callback functions ("views"). The regular expressions
use parenthesis to "capture" values from the URLs. When a user requests a page,
Django runs through each pattern, in order, and stops at the first one that
matches the requested URL. (If none of them matches, Django calls a
special-case 404 view.) This is blazingly fast, because the regular expressions
are compiled at load time.
The code above maps URL paths to Python callback functions ("views"). The path
strings use parameter tags to "capture" values from the URLs. When a user
requests a page, Django runs through each path, in order, and stops at the
first one that matches the requested URL. (If none of them matches, Django
calls a special-case 404 view.) This is blazingly fast, because the paths are
compiled into regular expressions at load time.
Once one of the regexes matches, Django calls the given view, which is a Python
function. Each view gets passed a request object -- which contains request
metadata -- and the values captured in the regex.
Once one of the URL patterns matches, Django calls the given view, which is a
Python function. Each view gets passed a request object -- which contains
request metadata -- and the values captured in the pattern.
For example, if a user requested the URL "/articles/2005/05/39323/", Django
would call the function ``news.views.article_detail(request,
'2005', '05', '39323')``.
year=2005, month=5, pk=39323)``.
Write your views
================

View file

@ -165,7 +165,7 @@ this. For a small app like polls, this process isn't too difficult.
2. Include the polls URLconf in your project urls.py like this::
url(r'^polls/', include('polls.urls')),
path('polls/', include('polls.urls')),
3. Run `python manage.py migrate` to create the polls models.

View file

@ -274,55 +274,45 @@ In the ``polls/urls.py`` file include the following code:
.. snippet::
:filename: polls/urls.py
from django.conf.urls import url
from django.urls import path
from . import views
urlpatterns = [
url(r'^$', views.index, name='index'),
path('', views.index, name='index'),
]
The next step is to point the root URLconf at the ``polls.urls`` module. In
``mysite/urls.py``, add an import for ``django.conf.urls.include`` and insert
an :func:`~django.conf.urls.include` in the ``urlpatterns`` list, so you have:
``mysite/urls.py``, add an import for ``django.urls.include`` and insert an
:func:`~django.urls.include` in the ``urlpatterns`` list, so you have:
.. snippet::
:filename: mysite/urls.py
from django.conf.urls import include, url
from django.urls import include, path
from django.contrib import admin
urlpatterns = [
url(r'^polls/', include('polls.urls')),
url(r'^admin/', admin.site.urls),
path('polls/', include('polls.urls')),
path('admin/', admin.site.urls),
]
The :func:`~django.conf.urls.include` function allows referencing other
URLconfs. Note that the regular expressions for the
:func:`~django.conf.urls.include` function doesn't have a ``$`` (end-of-string
match character) but rather a trailing slash. Whenever Django encounters
:func:`~django.conf.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.
The :func:`~django.urls.include` function allows referencing other URLconfs.
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.
The idea behind :func:`~django.conf.urls.include` is to make it easy to
The idea behind :func:`~django.urls.include` is to make it easy to
plug-and-play URLs. Since polls are in their own URLconf
(``polls/urls.py``), they can be placed under "/polls/", or under
"/fun_polls/", or under "/content/polls/", or any other path root, and the
app will still work.
.. admonition:: When to use :func:`~django.conf.urls.include()`
.. admonition:: When to use :func:`~django.urls.include()`
You should always use ``include()`` when you include other URL patterns.
``admin.site.urls`` is the only exception to this.
.. admonition:: Doesn't match what you see?
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:
@ -334,56 +324,39 @@ Go to http://localhost:8000/polls/ in your browser, and you should see the
text "*Hello, world. You're at the polls index.*", which you defined in the
``index`` view.
The :func:`~django.conf.urls.url` function is passed four arguments, two
required: ``regex`` and ``view``, and two optional: ``kwargs``, and ``name``.
The :func:`~django.urls.path` function is passed four arguments, two required:
``route`` and ``view``, and two optional: ``kwargs``, and ``name``.
At this point, it's worth reviewing what these arguments are for.
:func:`~django.conf.urls.url` argument: regex
:func:`~django.urls.path` argument: ``route``
---------------------------------------------
The term "regex" is a commonly used short form meaning "regular expression",
which is a syntax for matching patterns in strings, or in this case, url
patterns. Django starts at the first regular expression and makes its way down
the list, comparing the requested URL against each regular expression until it
finds one that matches.
``route`` is a string that contains a URL pattern. When processing a request,
Django starts at the first pattern in ``urlpatterns`` and makes its way down
the list, comparing the requested URL against each pattern until it finds one
that matches.
Note that these regular expressions do not search GET and POST parameters, or
the domain name. For example, in a request to
``https://www.example.com/myapp/``, the URLconf will look for ``myapp/``. In a
request to ``https://www.example.com/myapp/?page=3``, the URLconf will also
look for ``myapp/``.
Patterns don't search GET and POST parameters, or the domain name. For example,
in a request to ``https://www.example.com/myapp/``, the URLconf will look for
``myapp/``. In a request to ``https://www.example.com/myapp/?page=3``, the
URLconf will also look for ``myapp/``.
If you need help with regular expressions, see `Wikipedia's entry`_ and the
documentation of the :mod:`re` module. Also, the O'Reilly book "Mastering
Regular Expressions" by Jeffrey Friedl is fantastic. In practice, however,
you don't need to be an expert on regular expressions, as you really only need
to know how to capture simple patterns. In fact, complex regexes can have poor
lookup performance, so you probably shouldn't rely on the full power of regexes.
Finally, a performance note: these regular expressions are compiled the first
time the URLconf module is loaded. They're super fast (as long as the lookups
aren't too complex as noted above).
.. _Wikipedia's entry: https://en.wikipedia.org/wiki/Regular_expression
:func:`~django.conf.urls.url` argument: view
:func:`~django.urls.path` argument: ``view``
--------------------------------------------
When Django finds a regular expression match, Django calls the specified view
function, with an :class:`~django.http.HttpRequest` object as the first
argument and any “captured” values from the regular expression as other
arguments. If the regex uses simple captures, values are passed as positional
arguments; if it uses named captures, values are passed as keyword arguments.
We'll give an example of this in a bit.
When Django finds a matching pattern, it calls the specified view function with
an :class:`~django.http.HttpRequest` object as the first argument and any
"captured" values from the route as keyword arguments. We'll give an example
of this in a bit.
:func:`~django.conf.urls.url` argument: kwargs
:func:`~django.urls.path` argument: ``kwargs``
----------------------------------------------
Arbitrary keyword arguments can be passed in a dictionary to the target view. We
aren't going to use this feature of Django in the tutorial.
:func:`~django.conf.urls.url` argument: name
---------------------------------------------
:func:`~django.urls.path` argument: ``name``
--------------------------------------------
Naming your URL lets you refer to it unambiguously from elsewhere in Django,
especially from within templates. This powerful feature allows you to make

View file

@ -53,10 +53,10 @@ A URL pattern is simply the general form of a URL - for example:
``/newsarchive/<year>/<month>/``.
To get from a URL to a view, Django uses what are known as 'URLconfs'. A
URLconf maps URL patterns (described as regular expressions) to views.
URLconf maps URL patterns to views.
This tutorial provides basic instruction in the use of URLconfs, and you can
refer to :mod:`django.urls` for more information.
refer to :doc:`/topics/http/urls` for more information.
Writing more views
==================
@ -78,24 +78,24 @@ slightly different, because they take an argument:
return HttpResponse("You're voting on question %s." % question_id)
Wire these new views into the ``polls.urls`` module by adding the following
:func:`~django.conf.urls.url` calls:
:func:`~django.urls.path` calls:
.. snippet::
:filename: polls/urls.py
from django.conf.urls import url
from django.urls import path
from . import views
urlpatterns = [
# ex: /polls/
url(r'^$', views.index, name='index'),
path('', views.index, name='index'),
# ex: /polls/5/
url(r'^(?P<question_id>[0-9]+)/$', views.detail, name='detail'),
path('<int:question_id>/', views.detail, name='detail'),
# ex: /polls/5/results/
url(r'^(?P<question_id>[0-9]+)/results/$', views.results, name='results'),
path('<int:question_id>/results/', views.results, name='results'),
# ex: /polls/5/vote/
url(r'^(?P<question_id>[0-9]+)/vote/$', views.vote, name='vote'),
path('<int:question_id>/vote/', views.vote, name='vote'),
]
Take a look in your browser, at "/polls/34/". It'll run the ``detail()``
@ -106,26 +106,24 @@ placeholder results and voting pages.
When somebody requests a page from your website -- say, "/polls/34/", Django
will load the ``mysite.urls`` Python module because it's pointed to by the
:setting:`ROOT_URLCONF` setting. It finds the variable named ``urlpatterns``
and traverses the regular expressions in order. After finding the match at
``'^polls/'``, it strips off the matching text (``"polls/"``) and sends the
remaining text -- ``"34/"`` -- to the 'polls.urls' URLconf for further
processing. There it matches ``r'^(?P<question_id>[0-9]+)/$'``, resulting in a
call to the ``detail()`` view like so::
and traverses the patterns in order. After finding the match at ``'polls/'``,
it strips off the matching text (``"polls/"``) and sends the remaining text --
``"34/"`` -- to the 'polls.urls' URLconf for further processing. There it
matches ``'<int:question_id>/'``, resulting in a call to the ``detail()`` view
like so::
detail(request=<HttpRequest object>, question_id='34')
detail(request=<HttpRequest object>, question_id=34)
The ``question_id='34'`` part comes from ``(?P<question_id>[0-9]+)``. Using parentheses
around a pattern "captures" the text matched by that pattern and sends it as an
argument to the view function; ``?P<question_id>`` defines the name that will
be used to identify the matched pattern; and ``[0-9]+`` is a regular expression to
match a sequence of digits (i.e., a number).
The ``question_id=34`` part comes from ``<int:question_id>``. Using angle
brackets "captures" part of the URL and sends it as a keyword argument to the
view function. The ``:question_id>`` part of the string defines the name that
will be used to identify the matched pattern, and the ``<int:`` part is a
converter that determines what patterns should match this part of the URL path.
Because the URL patterns are regular expressions, there really is no limit on
what you can do with them. And there's no need to add URL cruft such as
``.html`` -- unless you want to, in which case you can do something like
this::
There's no need to add URL cruft such as ``.html`` -- unless you want to, in
which case you can do something like this::
url(r'^polls/latest\.html$', views.index),
path('polls/latest.html', views.index),
But, don't do that. It's silly.
@ -388,7 +386,7 @@ template, the link was partially hardcoded like this:
The problem with this hardcoded, tightly-coupled approach is that it becomes
challenging to change URLs on projects with a lot of templates. However, since
you defined the name argument in the :func:`~django.conf.urls.url` functions in
you defined the name argument in the :func:`~django.urls.path` functions in
the ``polls.urls`` module, you can remove a reliance on specific URL paths
defined in your url configurations by using the ``{% url %}`` template tag:
@ -402,7 +400,7 @@ defined below::
...
# the 'name' value as called by the {% url %} template tag
url(r'^(?P<question_id>[0-9]+)/$', views.detail, name='detail'),
path('<int:question_id>/', views.detail, name='detail'),
...
If you want to change the URL of the polls detail view to something else,
@ -411,7 +409,7 @@ template (or templates) you would change it in ``polls/urls.py``::
...
# added the word 'specifics'
url(r'^specifics/(?P<question_id>[0-9]+)/$', views.detail, name='detail'),
path('specifics/<int:question_id>/', views.detail, name='detail'),
...
Namespacing URL names
@ -430,16 +428,16 @@ file, go ahead and add an ``app_name`` to set the application namespace:
.. 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.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'),
path('', views.index, name='index'),
path('<int:question_id>/', views.detail, name='detail'),
path('<int:question_id>/results/', views.results, name='results'),
path('<int:question_id>/vote/', views.vote, name='vote'),
]
Now change your ``polls/index.html`` template from:

View file

@ -61,7 +61,7 @@ created a URLconf for the polls application that includes this line:
.. snippet::
:filename: polls/urls.py
url(r'^(?P<question_id>[0-9]+)/vote/$', views.vote, name='vote'),
path('<int:question_id>/vote/', views.vote, name='vote'),
We also created a dummy implementation of the ``vote()`` function. Let's
create a real version. Add the following to ``polls/views.py``:
@ -237,20 +237,20 @@ First, open the ``polls/urls.py`` URLconf and change it like so:
.. 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>[0-9]+)/$', views.DetailView.as_view(), name='detail'),
url(r'^(?P<pk>[0-9]+)/results/$', views.ResultsView.as_view(), name='results'),
url(r'^(?P<question_id>[0-9]+)/vote/$', views.vote, name='vote'),
path('', views.IndexView.as_view(), name='index'),
path('<int:pk>/', views.DetailView.as_view(), name='detail'),
path('<int:pk>/results/', views.ResultsView.as_view(), name='results'),
path('<int:question_id>/vote/', views.vote, name='vote'),
]
Note that the name of the matched pattern in the regexes of the second and third
patterns has changed from ``<question_id>`` to ``<pk>``.
Note that the name of the matched pattern in the path strings of the second and
third patterns has changed from ``<question_id>`` to ``<pk>``.
Amend views
-----------