2015年1月29日木曜日

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 url template tag.
• In Python code: Using the django.core.urlresolvers.reverse() function.
• In higher level code related to handling of URLs of Django model instances: The get_absolute_url()
method.
Examples
Consider again this URLconf entry:
from django.conf.urls import url
from . import views
urlpatterns = [
#...
url(r'^articles/([0-9]{4})/$', views.year_archive, name='news-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:
<a href="{% url 'news-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-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('news-year-archive', args=(year,)))
If, for some reason, it was decided that the URLs 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 identifier for it when comes the time of reversing URLs.
Read the next section to know about the solution Django provides for this.
Naming URL patterns
In order to perform URL reversing, you'll need to use named URL patterns as done in the examples above. The
string used for the URL name can contain any characters you like. You are not restricted to valid Python names.
When you name your URL patterns, make sure you use names that are unlikely to clash with any other application's
choice of names. If you call your URL pattern comment, and another application does the same thing, there's no
guarantee which URL will be inserted into your template when you use this name.
Putting a prefix on your URL names, perhaps derived from the application name, will decrease the chances of collision.
We recommend something like myapp-comment instead of comment.

0 件のコメント:

コメントを投稿