Skip to content

Commit

Permalink
Added section about URL reversion to URL mapper document.
Browse files Browse the repository at this point in the history
  • Loading branch information
ramiro committed Oct 7, 2012
1 parent 34a736b commit ec1aad1
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 21 deletions.
2 changes: 1 addition & 1 deletion docs/ref/contrib/formtools/form-wizard.txt
Expand Up @@ -226,7 +226,7 @@ Hooking the wizard into a URLconf
---------------------------------

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
:class:`~django.forms.Form` classes as an argument during instantiation::

Expand Down
12 changes: 9 additions & 3 deletions docs/ref/models/instances.txt
Expand Up @@ -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()``.

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! -->
<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>

Expand Down Expand Up @@ -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
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
contains a line such as::
Expand Down
2 changes: 1 addition & 1 deletion docs/ref/templates/builtins.txt
Expand Up @@ -997,7 +997,7 @@ refer to the name of the pattern in the ``url`` tag instead of using the
path to the view.

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.

If you'd like to retrieve a URL without displaying it, you can use a slightly
Expand Down
16 changes: 7 additions & 9 deletions docs/ref/urlresolvers.txt
Expand Up @@ -8,8 +8,7 @@ reverse()
---------

If you need to use something similar to the :ttag:`url` template tag in
your code, Django provides the following function (in the
:mod:`django.core.urlresolvers` module):
your code, Django provides the following function:

.. function:: reverse(viewname, [urlconf=None, args=None, kwargs=None, current_app=None])

Expand Down Expand Up @@ -59,15 +58,15 @@ You can use ``kwargs`` instead of ``args``. For example::

.. 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::

>>> reverse('cities', args=[u'Orléans'])
'.../Orl%C3%A9ans/'

Applying further encoding (such as :meth:`~django.utils.http.urlquote` or
``urllib.quote``) to the output of :meth:`~django.core.urlresolvers.reverse`
may produce undesirable results.
``urllib.quote``) to the output of ``reverse()`` may produce undesirable
results.

reverse_lazy()
--------------
Expand All @@ -94,9 +93,8 @@ URLConf is loaded. Some common cases where this function is necessary are:
resolve()
---------

The :func:`django.core.urlresolvers.resolve` function can be used for
resolving URL paths to the corresponding view functions. It has the
following signature:
The ``resolve()`` function can be used for resolving URL paths to the
corresponding view functions. It has the following signature:

.. function:: resolve(path, urlconf=None)

Expand Down Expand Up @@ -184,7 +182,7 @@ whether a view would raise a ``Http404`` error before redirecting to it::
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
``get_absolute_url()`` method. See :func:`django.db.models.permalink` for more.

Expand Down
119 changes: 113 additions & 6 deletions docs/topics/http/urls.txt
Expand Up @@ -421,9 +421,9 @@ options to views.
Passing extra options to ``include()``
--------------------------------------

Similarly, you can pass extra options to ``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.conf.urls.include`.
When you pass extra options to ``include()``, *each* line in the included
URLconf will be passed the extra options.

For example, these two URLconf sets are functionally identical:

Expand Down Expand Up @@ -510,6 +510,103 @@ imported::
(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
Expand Down Expand Up @@ -689,9 +786,10 @@ URL namespaces and included URLconfs

URL namespaces of included URLconfs can be specified in two ways.

Firstly, you can provide the application and :term:`instance namespace` as
arguments to :func:`django.conf.urls.include()` when you construct your URL
patterns. For example,::
Firstly, you can provide the :term:`application <application namespace>` and
:term:`instance <instance namespace>` namespaces as arguments to
:func:`django.conf.urls.include()` when you construct your URL patterns. For
example,::

(r'^help/', include('apps.help.urls', namespace='foo', app_name='bar')),

Expand All @@ -706,6 +804,15 @@ However, you can also ``include()`` a 3-tuple containing::

(<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
instance namespace.

Expand Down
2 changes: 1 addition & 1 deletion docs/topics/testing.txt
Expand Up @@ -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
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::

>>> response = c.get('/redirect_me/', follow=True)
Expand Down

0 comments on commit ec1aad1

Please sign in to comment.