Added section about URL reversion to URL mapper document.

This commit is contained in:
Ramiro Morales 2012-10-07 20:11:12 -03:00
parent 34a736b752
commit ec1aad1671
6 changed files with 132 additions and 21 deletions

View File

@ -226,7 +226,7 @@ Hooking the wizard into a URLconf
--------------------------------- ---------------------------------
Finally, we need to specify which forms to use in the wizard, and then Finally, we need to specify which forms to use in the wizard, and then
deploy the new :class:`WizardView` object at an URL in the ``urls.py``. The deploy the new :class:`WizardView` object at a URL in the ``urls.py``. The
wizard's :meth:`as_view` method takes a list of your wizard's :meth:`as_view` method takes a list of your
:class:`~django.forms.Form` classes as an argument during instantiation:: :class:`~django.forms.Form` classes as an argument during instantiation::

View File

@ -494,12 +494,16 @@ defined. If it makes sense for your model's instances to each have a unique
URL, you should define ``get_absolute_url()``. URL, you should define ``get_absolute_url()``.
It's good practice to use ``get_absolute_url()`` in templates, instead of It's good practice to use ``get_absolute_url()`` in templates, instead of
hard-coding your objects' URLs. For example, this template code is bad:: hard-coding your objects' URLs. For example, this template code is bad:
.. code-block:: html+django
<!-- BAD template code. Avoid! --> <!-- BAD template code. Avoid! -->
<a href="/people/{{ object.id }}/">{{ object.name }}</a> <a href="/people/{{ object.id }}/">{{ object.name }}</a>
This template code is much better:: This template code is much better:
.. code-block:: html+django
<a href="{{ object.get_absolute_url }}">{{ object.name }}</a> <a href="{{ object.get_absolute_url }}">{{ object.name }}</a>
@ -535,7 +539,9 @@ pattern name) and a list of position or keyword arguments and uses the URLconf
patterns to construct the correct, full URL. It returns a string for the patterns to construct the correct, full URL. It returns a string for the
correct URL, with all parameters substituted in the correct positions. correct URL, with all parameters substituted in the correct positions.
The ``permalink`` decorator is a Python-level equivalent to the :ttag:`url` template tag and a high-level wrapper for the :func:`django.core.urlresolvers.reverse()` function. The ``permalink`` decorator is a Python-level equivalent to the :ttag:`url`
template tag and a high-level wrapper for the
:func:`django.core.urlresolvers.reverse()` function.
An example should make it clear how to use ``permalink()``. Suppose your URLconf An example should make it clear how to use ``permalink()``. Suppose your URLconf
contains a line such as:: contains a line such as::

View File

@ -997,7 +997,7 @@ refer to the name of the pattern in the ``url`` tag instead of using the
path to the view. path to the view.
Note that if the URL you're reversing doesn't exist, you'll get an Note that if the URL you're reversing doesn't exist, you'll get an
:exc:`^django.core.urlresolvers.NoReverseMatch` exception raised, which will :exc:`~django.core.urlresolvers.NoReverseMatch` exception raised, which will
cause your site to display an error page. cause your site to display an error page.
If you'd like to retrieve a URL without displaying it, you can use a slightly If you'd like to retrieve a URL without displaying it, you can use a slightly

View File

@ -8,8 +8,7 @@ reverse()
--------- ---------
If you need to use something similar to the :ttag:`url` template tag in If you need to use something similar to the :ttag:`url` template tag in
your code, Django provides the following function (in the your code, Django provides the following function:
:mod:`django.core.urlresolvers` module):
.. function:: reverse(viewname, [urlconf=None, args=None, kwargs=None, current_app=None]) .. function:: reverse(viewname, [urlconf=None, args=None, kwargs=None, current_app=None])
@ -59,15 +58,15 @@ You can use ``kwargs`` instead of ``args``. For example::
.. note:: .. note::
The string returned by :meth:`~django.core.urlresolvers.reverse` is already The string returned by ``reverse()`` is already
:ref:`urlquoted <uri-and-iri-handling>`. For example:: :ref:`urlquoted <uri-and-iri-handling>`. For example::
>>> reverse('cities', args=[u'Orléans']) >>> reverse('cities', args=[u'Orléans'])
'.../Orl%C3%A9ans/' '.../Orl%C3%A9ans/'
Applying further encoding (such as :meth:`~django.utils.http.urlquote` or Applying further encoding (such as :meth:`~django.utils.http.urlquote` or
``urllib.quote``) to the output of :meth:`~django.core.urlresolvers.reverse` ``urllib.quote``) to the output of ``reverse()`` may produce undesirable
may produce undesirable results. results.
reverse_lazy() reverse_lazy()
-------------- --------------
@ -94,9 +93,8 @@ URLConf is loaded. Some common cases where this function is necessary are:
resolve() resolve()
--------- ---------
The :func:`django.core.urlresolvers.resolve` function can be used for The ``resolve()`` function can be used for resolving URL paths to the
resolving URL paths to the corresponding view functions. It has the corresponding view functions. It has the following signature:
following signature:
.. function:: resolve(path, urlconf=None) .. function:: resolve(path, urlconf=None)
@ -184,7 +182,7 @@ whether a view would raise a ``Http404`` error before redirecting to it::
permalink() permalink()
----------- -----------
The :func:`django.db.models.permalink` decorator is useful for writing short The :func:`~django.db.models.permalink` decorator is useful for writing short
methods that return a full URL path. For example, a model's methods that return a full URL path. For example, a model's
``get_absolute_url()`` method. See :func:`django.db.models.permalink` for more. ``get_absolute_url()`` method. See :func:`django.db.models.permalink` for more.

View File

@ -421,9 +421,9 @@ options to views.
Passing extra options to ``include()`` Passing extra options to ``include()``
-------------------------------------- --------------------------------------
Similarly, you can pass extra options to ``include()``. When you pass extra Similarly, you can pass extra options to :func:`~django.conf.urls.include`.
options to ``include()``, *each* line in the included URLconf will be passed When you pass extra options to ``include()``, *each* line in the included
the extra options. URLconf will be passed the extra options.
For example, these two URLconf sets are functionally identical: For example, these two URLconf sets are functionally identical:
@ -510,6 +510,103 @@ imported::
(r'^myview/$', ClassBasedView.as_view()), (r'^myview/$', ClassBasedView.as_view()),
) )
Reverse resolution of URLs
==========================
A common need when working on a Django project is the possibility to obtain URLs
in their final forms either for embedding in generated content (views and assets
URLs, URLs shown to the user, etc.) or for handling of the navigation flow on
the server side (redirections, etc.)
It is strongly desirable not having to hard-code these URLs (a laborious,
non-scalable and error-prone strategy) or having to devise ad-hoc mechanisms for
generating URLs that are parallel to the design described by the URLconf and as
such in danger of producing stale URLs at some point.
In other words, what's needed is a DRY mechanism. Among other advantages it
would allow evolution of the URL design without having to go all over the
project source code to search and replace outdated URLs.
The piece of information we have available as a starting point to get a URL is
an identification (e.g. the name) of the view in charge of handling it, other
pieces of information that necessarily must participate in the lookup of the
right URL are the types (positional, keyword) and values of the view arguments.
Django provides a solution such that the URL mapper is the only repository of
the URL design. You feed it with your URLconf and then it can be used in both
directions:
* Starting with a URL requested by the user/browser, it calls the right Django
view providing any arguments it might need with their values as extracted from
the URL.
* Starting with the identification of the corresponding Django view plus the
values of arguments that would be passed to it, obtain the associated URL.
The first one is the usage we've been discussing in the previous sections. The
second one is what is known as *reverse resolution of URLs*, *reverse URL
matching*, *reverse URL lookup*, or simply *URL reversing*.
Django provides tools for performing URL reversing that match the different
layers where URLs are needed:
* In templates: Using the :ttag:`url` template tag.
* In Python code: Using the :func:`django.core.urlresolvers.reverse()`
function.
* In higher level code related to handling of URLs of Django model instances:
The :meth:`django.db.models.Model.get_absolute_url()` method and the
:func:`django.db.models.permalink` decorator.
Examples
--------
Consider again this URLconf entry::
from django.conf.urls import patterns, url
urlpatterns = patterns('',
#...
url(r'^articles/(\d{4})/$', 'news.views.year_archive'),
#...
)
According to this design, the URL for the archive corresponding to year *nnnn*
is ``/articles/nnnn/``.
You can obtain these in template code by using:
.. code-block:: html+django
<a href="{% url 'news.views.year_archive' 2012 %}">2012 Archive</a>
{# Or with the year in a template context variable: #}
<ul>
{% for yearvar in year_list %}
<li><a href="{% url 'news.views.year_archive' yearvar %}">{{ yearvar }} Archive</a></li>
{% endfor %}
</ul>
Or in Python code::
from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect
def redirect_to_year(request):
# ...
year = 2006
# ...
return HttpResponseRedirect(reverse('new.views.year_archive', args=(year,)))
If, for some reason, it was decided that the URL where content for yearly
article archives are published at should be changed then you would only need to
change the entry in the URLconf.
In some scenarios where views are of a generic nature, a many-to-one
relationship might exist between URLs and views. For these cases the view name
isn't a good enough identificator for it when it comes the time of reversing
URLs. Read the next section to know about the solution Django provides for this.
.. _naming-url-patterns: .. _naming-url-patterns:
Naming URL patterns Naming URL patterns
@ -689,9 +786,10 @@ URL namespaces and included URLconfs
URL namespaces of included URLconfs can be specified in two ways. URL namespaces of included URLconfs can be specified in two ways.
Firstly, you can provide the application and :term:`instance namespace` as Firstly, you can provide the :term:`application <application namespace>` and
arguments to :func:`django.conf.urls.include()` when you construct your URL :term:`instance <instance namespace>` namespaces as arguments to
patterns. For example,:: :func:`django.conf.urls.include()` when you construct your URL patterns. For
example,::
(r'^help/', include('apps.help.urls', namespace='foo', app_name='bar')), (r'^help/', include('apps.help.urls', namespace='foo', app_name='bar')),
@ -706,6 +804,15 @@ However, you can also ``include()`` a 3-tuple containing::
(<patterns object>, <application namespace>, <instance namespace>) (<patterns object>, <application namespace>, <instance namespace>)
For example::
help_patterns = patterns('',
url(r'^basic/$', 'apps.help.views.views.basic'),
url(r'^advanced/$', 'apps.help.views.views.advanced'),
)
(r'^help/', include(help_patterns, 'bar', 'foo')),
This will include the nominated URL patterns into the given application and This will include the nominated URL patterns into the given application and
instance namespace. instance namespace.

View File

@ -769,7 +769,7 @@ Use the ``django.test.client.Client`` class to make requests.
and a ``redirect_chain`` attribute will be set in the response object and a ``redirect_chain`` attribute will be set in the response object
containing tuples of the intermediate urls and status codes. containing tuples of the intermediate urls and status codes.
If you had an url ``/redirect_me/`` that redirected to ``/next/``, that If you had a URL ``/redirect_me/`` that redirected to ``/next/``, that
redirected to ``/final/``, this is what you'd see:: redirected to ``/final/``, this is what you'd see::
>>> response = c.get('/redirect_me/', follow=True) >>> response = c.get('/redirect_me/', follow=True)