From ec1aad1671bfbba7ef58e7477dd14d7add065838 Mon Sep 17 00:00:00 2001 From: Ramiro Morales Date: Sun, 7 Oct 2012 20:11:12 -0300 Subject: [PATCH] Added section about URL reversion to URL mapper document. --- docs/ref/contrib/formtools/form-wizard.txt | 2 +- docs/ref/models/instances.txt | 12 ++- docs/ref/templates/builtins.txt | 2 +- docs/ref/urlresolvers.txt | 16 ++- docs/topics/http/urls.txt | 119 +++++++++++++++++++-- docs/topics/testing.txt | 2 +- 6 files changed, 132 insertions(+), 21 deletions(-) diff --git a/docs/ref/contrib/formtools/form-wizard.txt b/docs/ref/contrib/formtools/form-wizard.txt index d5231de3e5173..0ced1bf1559f5 100644 --- a/docs/ref/contrib/formtools/form-wizard.txt +++ b/docs/ref/contrib/formtools/form-wizard.txt @@ -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:: diff --git a/docs/ref/models/instances.txt b/docs/ref/models/instances.txt index 92fc4ef31af1d..1ba41148b0951 100644 --- a/docs/ref/models/instances.txt +++ b/docs/ref/models/instances.txt @@ -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 {{ object.name }} -This template code is much better:: +This template code is much better: + +.. code-block:: html+django {{ object.name }} @@ -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:: diff --git a/docs/ref/templates/builtins.txt b/docs/ref/templates/builtins.txt index 07ac2849050b2..3b8d058fb4e17 100644 --- a/docs/ref/templates/builtins.txt +++ b/docs/ref/templates/builtins.txt @@ -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 diff --git a/docs/ref/urlresolvers.txt b/docs/ref/urlresolvers.txt index 965cafb29ba51..1bb33c7ca1709 100644 --- a/docs/ref/urlresolvers.txt +++ b/docs/ref/urlresolvers.txt @@ -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]) @@ -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 `. 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() -------------- @@ -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) @@ -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. diff --git a/docs/topics/http/urls.txt b/docs/topics/http/urls.txt index d7b3b03d84668..c51ce2d2a4031 100644 --- a/docs/topics/http/urls.txt +++ b/docs/topics/http/urls.txt @@ -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: @@ -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 + + 2012 Archive + {# Or with the year in a template context variable: #} + + +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 @@ -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 ` and +:term:`instance ` 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')), @@ -706,6 +804,15 @@ However, you can also ``include()`` a 3-tuple containing:: (, , ) +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. diff --git a/docs/topics/testing.txt b/docs/topics/testing.txt index 895e721ef550e..e2d424aec591e 100644 --- a/docs/topics/testing.txt +++ b/docs/topics/testing.txt @@ -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)