Une organisation propre et élégante des URL est un aspect important dans une application Web de qualité. Django vous laisse organiser vos URL comme bon vous semble, sans restriction imposée par le système.
Pas besoin de .php ou de .cgi, et encore moins de 0,2097,1-1-1928,00.
Lisez Cool URIs don’t change, par le père du World Wide Web Tim Berners-Lee, pour savoir pourquoi les URL devraient être claires et conviviales.
Pour organiser les URL d’une application, il s’agit de créer un module Python appelé communément URLconf (configuration d’URL). Le code de ce module est en Python pur et est une simple correspondance entre motifs d’URL (de banales expressions régulières) et fonctions Python (vos vues).
Cette correspondance peut être autant courte que longue. Elle peut faire référence à d’autres correspondances. Et étant donné qu’il s’agit de code Python pur, elle peut être construite dynamiquement.
Django fournit également une façon de traduire les URL en fonction de la langue active. Voir la documentation sur l’internationalisation pour plus d’informations.
Quand un utilisateur accède à une page de votre site Django, voici l’algorithme que le système suit pour déterminer quel code Python doit être exécuté :
Django identifie le module URLconf racine à utiliser. Par défaut, c’est la valeur attribuée au réglage ROOT_URLCONF, mais si la requête HttpRequest entrante a un attribut urlconf (défini par l’intergiciel de traitement de requête), sa valeur sera utilisée en lieu et place du réglage ROOT_URLCONF.
Django charge ce module Python et cherche la variable urlpatterns. Ce devrait être une liste Python, au format renvoyé par la fonction django.conf.urls.patterns().
Django parcourt chaque motif d’URL dans l’ordre et s’arrête dès la première correspondance avec l’URL demandée.
Une fois qu’une des expressions régulières correspond, Django importe et appelle la vue correspondante, qui est une simple fonction Python (ou une vue reposant sur une classe). La vue se voit passer les paramètres suivants :
Une instance HttpRequest.
Si l’expression régulière correspondante n’a pas renvoyé de groupes nommés, les correspondances de l’expression régulière sont transmises comme paramètres positionnels.
Les paramètres nommés sont formés des groupes nommés résultant de la correspondance par expression régulière, surchargés par tout paramètre ajouté dans le paramètre facultatif kwargs de django.conf.urls.url().
Si aucune expression régulière ne correspond, ou si une exception est levée durant ce processus, Django appelle une vue d’erreur appropriée. Voir Gestion d’erreur plus bas.
Voici un exemple d’URLconf :
from django.conf.urls import patterns, url
urlpatterns = patterns('',
url(r'^articles/2003/$', 'news.views.special_case_2003'),
url(r'^articles/(\d{4})/$', 'news.views.year_archive'),
url(r'^articles/(\d{4})/(\d{2})/$', 'news.views.month_archive'),
url(r'^articles/(\d{4})/(\d{2})/(\d+)/$', 'news.views.article_detail'),
)
Notes :
Pour capturer une valeur contenue dans l’URL, entourez-la simplement de parenthèses.
Inutile de préfixer vos URL d’une barre oblique, elles en ont toutes une. Par exemple, écrivez ^articles et non ^/articles.
Le 'r' devant chaque expression régulière est facultatif mais recommandé. Il signale à Python qu’une chaîne est « brute », aucun caractère de la chaîne ne doit être échappé. Voir l’explication de Dive Into Python.
Exemples de requêtes :
Une requête sur /articles/2005/03/ correspondrait à la troisième entrée dans la liste. Django appellerait la fonction news.views.month_archive(request, '2005', '03').
/articles/2005/3/ ne correspondrait à aucun motif d’URL, car la troisième entrée dans la liste nécessite deux chiffres pour le mois.
/articles/2003/ correspondrait au premier motif de la liste, et non le deuxième, car les motifs sont évalués dans l’ordre, et le premier est le premier à correspondre. Libre à vous d’utiliser l’ordre de définition pour traiter des cas spéciaux comme ici.
/articles/2003 ne correspondrait à aucun motif, car chaque motif nécessite que l’URL se termine par une barre oblique.
/articles/2003/03/03/ correspondrait au dernier motif. Django appellerait la fonction news.views.article_detail(request, '2003', '03', '03').
L’exemple ci-dessus utilise de simples groupes d’expressions régulières non nommés (via les parenthèses) pour capturer des bouts d’URL et les passer en tant que paramètres positionnels à une vue. Dans des situations plus poussées, il est possible d’utiliser des groupes de capture nommés pour capturer des bouts d’URL et les passer en tant que paramètres nommés à une vue.
La syntaxe des expressions régulières en Python pour les groupes de captures nommés est (?P<nom>motif), où nom est le nom du groupe et motif est le motif à faire correspondre.
Voici le même exemple d’URLconf que ci-dessus, réécrit en utilisant les groupes nommés :
from django.conf.urls import patterns, url
urlpatterns = patterns('',
url(r'^articles/2003/$', 'news.views.special_case_2003'),
url(r'^articles/(?P<year>\d{4})/$', 'news.views.year_archive'),
url(r'^articles/(?P<year>\d{4})/(?P<month>\d{2})/$', 'news.views.month_archive'),
url(r'^articles/(?P<year>\d{4})/(?P<month>\d{2})/(?P<day>\d{2})/$', 'news.views.article_detail'),
)
Le résultat est strictement identique qu’avec le précédent exemple, avec une subtile différence : les valeurs capturées sont passées aux vues en tant que paramètres nommés plutôt qu’en tant que paramètres positionnels. Par exemple :
Une requête sur /articles/2005/03/ appellerait la fonction news.views.month_archive(request, year='2005', month='03'), plutôt que news.views.month_archive(request, '2005', '03').
Une requête sur /articles/2003/03/03/ appellerait la fonction news.views.article_detail(request, year='2003', month='03', day='03').
En pratique, cela signifie que vos modules URLconf sont légèrement plus explicites et moins sujets aux bogues d’ordre des paramètres, et vous pouvez réordonner les paramètres des définitions de vos vues. Bien sûr, ces avantages viennent au prix de la brièveté ; certains développeurs trouvent la syntaxe des groupes nommés vilaine et trop verbeuse.
Voici l’algorithme que suit l’analyseur d’URLconf, en faisant la distinction entre les groupes nommés et les groupes non nommés dans une expression régulière :
S’il y a des paramètres nommés, il les utilise et ignore les paramètres anonymes.
Sinon, il passe tous les paramètres anonymes en tant que paramètres positionnels.
Dans les deux cas, tout paramètre nommé supplémentaire passé comme décrit dans Transmission de paramètres supplémentaires à une vue (plus bas) sera aussi passé à la vue.
L’URLconf utilise l’URL demandée, en tant que simple chaîne Python. Ni les paramètres GET ou POST, ni le nom de domaine ne sont inclus.
Par exemple, pour une requête sur http://www.example.com/myapp/, l’URLconf cherchera myapp/.
Pour une requête sur http://www.example.com/myapp/?page=3, l’URLconf cherchera myapp/.
L’URLconf ignore la méthode de la requête. En d’autres termes, toutes les méthodes de requêtes – POST, GET, HEAD, etc. – seront acheminées vers la même fonction de la même URL.
Chaque paramètre capturé est envoyé à la vue en tant que simple chaîne de caractères Python, peu importe la correspondance qu’effectue l’expression régulière. Par exemple, dans cette ligne de configuration d’URL :
url(r'^articles/(?P<year>\d{4})/$', 'news.views.year_archive'),
... le paramètre year de news.views.year_archive() est une chaîne de caractères, pas un entier, même si \d{4} ne correspond qu’à des chaînes représentant des entiers.
Une astuce pratique est de définir des valeurs par défaut pour les paramètres de vos vues. Voici un exemple de configuration d’URL et de vue :
# URLconf
from django.conf.urls import patterns, url
urlpatterns = patterns('',
url(r'^blog/$', 'blog.views.page'),
url(r'^blog/page(?P<num>\d+)/$', 'blog.views.page'),
)
# View (in blog/views.py)
def page(request, num="1"):
# Output the appropriate page of blog entries, according to num.
...
Dans l’exemple ci-dessus, les deux motifs d’URL pointent vers la même vue – blog.views.page – mais le premier motif ne capture aucun élément de l’URL. Si le premier motif correspond, la fonction page() utilisera la valeur par défaut de son paramètre num, "1". Si le second motif correspond, page() utilisera la valeur capturée par l’expression régulière.
Chaque expression régulière d’un urlpatterns est compilée la première fois qu’elle est utilisée. Cela rend le système extrêmement rapide.
urlpatterns doit être une liste Python, au format renvoyé par la fonction django.conf.urls.patterns(). Utilisez toujours patterns() pour créer la variable urlpatterns.
Quand Django ne trouve pas d’expression régulière correspondant à l’URL demandée, ou quand une exception est générée, Django appelle une vue de gestion d’erreur.
Les vues à utiliser pour ces situations sont définies par quatre variables. Leur valeur par défaut devraient convenir à la plupart des projets, mais il reste possible de les personnaliser en leur attribuant des valeurs.
Voir la documentation sur la personnalisation des vues d’erreur pour plus de détails.
Ces valeurs doivent être définies dans l’URLconf racine. La définition de ces variables dans n’importe quel autre URLconf n’aura aucun effet.
Les valeurs doivent pouvoir être appelées, ou être des chaînes de caractères représentant le chemin d’importation Python complet vers une vue qui doit être appelée pour traiter l’erreur actuelle.
Les variables sont :
handler404 – Voir django.conf.urls.handler404.
handler500 – Voir django.conf.urls.handler500.
handler403 – Voir django.conf.urls.handler403.
handler400 – Voir django.conf.urls.handler400.
Vous pouvez définir un préfixe commun à vos vues lors de l’appel à patterns(), afin de limiter la répétition de code.
Voici l’exemple d’URLconf présenté dans Présentation de Django:
from django.conf.urls import patterns, url
urlpatterns = patterns('',
url(r'^articles/(\d{4})/$', 'news.views.year_archive'),
url(r'^articles/(\d{4})/(\d{2})/$', 'news.views.month_archive'),
url(r'^articles/(\d{4})/(\d{2})/(\d+)/$', 'news.views.article_detail'),
)
Dans cet exemple, chaque vue a un préfixe commun – 'news.views'. Au lieu de le taper pour chaque élément de la variable urlpatterns, vous pouvez utiliser le premier paramètre de la fonction patterns() pour définir un préfixe à appliquer à chaque vue.
Avec ceci, l’exemple ci-dessus peut être écrit de façon plus concise :
from django.conf.urls import patterns, url
urlpatterns = patterns('news.views',
url(r'^articles/(\d{4})/$', 'year_archive'),
url(r'^articles/(\d{4})/(\d{2})/$', 'month_archive'),
url(r'^articles/(\d{4})/(\d{2})/(\d+)/$', 'article_detail'),
)
Remarquez que l’on ne met pas de point (".") à la fin du préfixe. Django s’en charge automatiquement.
En pratique, vous allez probablement mélanger vos vues à tel point que les vues de votre urlpatterns``n'auront plus un seul préfixe commun. Toutefois, vous pouvez tout de même profiter du raccourci offert par le préfixe de vue pour éliminer la répétition de code. Il suffit de combiner plusieurs objets ``patterns(), comme ceci :
Avant :
from django.conf.urls import patterns, url
urlpatterns = patterns('',
url(r'^$', 'myapp.views.app_index'),
url(r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/$', 'myapp.views.month_display'),
url(r'^tag/(?P<tag>\w+)/$', 'weblog.views.tag'),
)
Après :
from django.conf.urls import patterns, url
urlpatterns = patterns('myapp.views',
url(r'^$', 'app_index'),
url(r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/$','month_display'),
)
urlpatterns += patterns('weblog.views',
url(r'^tag/(?P<tag>\w+)/$', 'tag'),
)
À tout moment, votre urlpatterns peut « inclure » d’autres modules URLconf. Fondamentalement, cela permet de rassembler un ensemble d’URL les unes sous les autres.
Par exemple, voici un extrait de la configuration d’URL du site Web Django lui-même. Il inclut un certain nombre d’autres configurations d’URL :
from django.conf.urls import include, patterns, url
urlpatterns = patterns('',
# ... snip ...
url(r'^comments/', include('django.contrib.comments.urls')),
url(r'^community/', include('django_website.aggregator.urls')),
url(r'^contact/', include('django_website.contact.urls')),
# ... snip ...
)
Remarquez que les expressions régulières de cet exemple n’ont pas de $ (caractère de fin de correspondance) mais ont bien une barre oblique finale. Quand Django rencontre un include() (django.conf.urls.include()), il tronque le bout d’URL qui correspondait jusque là et passe la chaîne de caractères restante à la configuration d’URL incluse pour continuer le traitement.
Une autre possibilité est d’inclure d’autres motifs d’URL non pas en spécifiant le module Python URLconf les définissant en tant que paramètre de include(), mais en utilisant directement la liste de motifs renvoyée par patterns() à la place. Par exemple, dans cet URLconf:
from django.conf.urls import include, patterns, url
extra_patterns = patterns('',
url(r'^reports/(?P<id>\d+)/$', 'credit.views.report'),
url(r'^charge/$', 'credit.views.charge'),
)
urlpatterns = patterns('',
url(r'^$', 'apps.main.views.homepage'),
url(r'^help/', include('apps.help.urls')),
url(r'^credit/', include(extra_patterns)),
)
Dans cet exemple, l’URL /credit/reports/ sera traitée par la vue Django credit.views.report().
Cela peut être utilisé pour supprimer de la redondance dans des configurations d’URL où un préfixe de motif est employé de manière répétitive. Par exemple, considérez cette configuration d’URL :
from django.conf.urls import patterns, url
urlpatterns = patterns('wiki.views',
url(r'^(?P<page_slug>\w+)-(?P<page_id>\w+)/history/$', 'history'),
url(r'^(?P<page_slug>\w+)-(?P<page_id>\w+)/edit/$', 'edit'),
url(r'^(?P<page_slug>\w+)-(?P<page_id>\w+)/discuss/$', 'discuss'),
url(r'^(?P<page_slug>\w+)-(?P<page_id>\w+)/permissions/$', 'permissions'),
)
Nous pouvons l’améliorer en indiquant une seule fois le préfixe de chemin commun et en regroupant les suffixes qui diffèrent :
from django.conf.urls import include, patterns, url
urlpatterns = patterns('',
url(r'^(?P<page_slug>\w+)-(?P<page_id>\w+)/', include(patterns('wiki.views',
url(r'^history/$', 'history'),
url(r'^edit/$', 'edit'),
url(r'^discuss/$', 'discuss'),
url(r'^permissions/$', 'permissions'),
))),
)
Un URLconf inclus reçoit tous les paramètres capturés par ses URLconf parents, ainsi l’exemple suivant est valide :
# In settings/urls/main.py
from django.conf.urls import include, patterns, url
urlpatterns = patterns('',
url(r'^(?P<username>\w+)/blog/', include('foo.urls.blog')),
)
# In foo/urls/blog.py
from django.conf.urls import patterns, url
urlpatterns = patterns('foo.views',
url(r'^$', 'blog.index'),
url(r'^archive/$', 'blog.archive'),
)
Dans l’exemple ci-dessus, la variable capturée "username" est passée à l’URLconf inclus, comme on peut s’y attendre.
Les configurations d’URL ont un point d’entrée qui permet de passer des paramètres supplémentaires à vos vues, via un dictionnaire Python.
La fonction django.conf.urls.url() accepte un troisième paramètre facultatif qui doit correspondre à un dictionnaire de paramètres nommés supplémentaires à transmettre à la fonction de vue.
Par exemple :
from django.conf.urls import patterns, url
urlpatterns = patterns('blog.views',
url(r'^blog/(?P<year>\d{4})/$', 'year_archive', {'foo': 'bar'}),
)
Dans cet exemple, si une requête demande /blog/2005/, Django appelle blog.views.year_archive(request, year='2005', foo='bar').
Cette technique est utilisée dans le système de syndication pour passer des métadonnées et des options aux vues.
Gestion des confilts
Il est possible d’avoir un motif d’URL qui capture des paramètres nommés, et qui passe aussi des paramètres ayant les mêmes noms dans son dictionnaire de paramètres supplémentaires. Quand c’est le cas, les paramètres du dictionnaire seront utilisés à la place des paramètres capturés dans l’URL.
De la même façon, vous pouvez passer des paramètres supplémentaires à la fonction include(). Dans ce cas, chacune des lignes de la configuration d’URL incluse recevra les paramètres supplémentaires.
Par exemple, ces deux ensembles de configurations d’URL sont fonctionnellement identiques :
Premier ensemble :
# main.py
from django.conf.urls import include, patterns, url
urlpatterns = patterns('',
url(r'^blog/', include('inner'), {'blogid': 3}),
)
# inner.py
from django.conf.urls import patterns, url
urlpatterns = patterns('',
url(r'^archive/$', 'mysite.views.archive'),
url(r'^about/$', 'mysite.views.about'),
)
Second ensemble :
# main.py
from django.conf.urls import include, patterns, url
urlpatterns = patterns('',
url(r'^blog/', include('inner')),
)
# inner.py
from django.conf.urls import patterns, url
urlpatterns = patterns('',
url(r'^archive/$', 'mysite.views.archive', {'blogid': 3}),
url(r'^about/$', 'mysite.views.about', {'blogid': 3}),
)
Remarquez que les paramètres supplémentaires seront toujours passés à chaque élément de la configuration d’URL incluse, peu importe si la vue associée à l’élément accepte ces paramètres comme étant valides. C’est pour cette raison que cette technique est utile seulement lorsque vous êtes certain que toutes les vues de la configuration d’URL incluse acceptent les paramètres supplémentaires que vous passez.
Certains développeurs trouvent plus naturel de passer la fonction Python en tant qu’objet plutôt qu’une chaîne contenant le chemin vers son module. Cette alternative est prise en charge – vous pouvez passer n’importe quel objet qu’il est possible d’appeler en tant que vue.
Prenons pour exemple cette configuration d’URL utilisant la notation « chaîne » :
from django.conf.urls import patterns, url
urlpatterns = patterns('',
url(r'^archive/$', 'mysite.views.archive'),
url(r'^about/$', 'mysite.views.about'),
url(r'^contact/$', 'mysite.views.contact'),
)
Vous pouvez accomplir exactement la même chose en passant des objets plutôt que des chaînes. Soyez simplement sûr de bien importer les objets :
from django.conf.urls import patterns, url
from mysite.views import archive, about, contact
urlpatterns = patterns('',
url(r'^archive/$', archive),
url(r'^about/$', about),
url(r'^contact/$', contact),
)
L’exemple suivant est fonctionnellement identique. Il est juste un peu plus compact car il importe le module contenant les vues, plutôt que chacune des vues :
from django.conf.urls import patterns, url
from mysite import views
urlpatterns = patterns('',
url(r'^archive/$', views.archive),
url(r'^about/$', views.about),
url(r'^contact/$', views.contact),
)
Le style que vous adoptez ne dépend que de vous.
Remarquez que si vous utilisez cette technique – passer des objets plutôt que des chaînes – le préfixe de vue (tel qu’expliqué plus haut dans « Le préfixe de vue ») n’aura aucun effet.
Remarquez que les vues fondées sur les classes doivent être importées :
from django.conf.urls import patterns, url
from mysite.views import ClassBasedView
urlpatterns = patterns('',
url(r'^myview/$', ClassBasedView.as_view()),
)
Un besoin courant quand on travaille sur un projet Django est d’avoir la possibilité d’obtenir des URL dans leur forme finale soit pour les incorporer dans du contenu généré (des URL vers des vues et des ressources, des URL montrées à l’utilisateur, etc.) ou pour gérer le flux de navigation côté serveur (redirections, etc.).
Il est très fortement suggéré de ne pas écrire des URL figées (une approche laborieuse, non extensible et sujette à l’erreur), ni de concevoir des mécanismes ad hoc capables de générer des URL identiques à celles décrites dans la configuration d’URL, risquant de générer des URL mortes.
En d’autres termes, ce dont nous avons besoin est un mécanisme DRY (« ne pas se répéter »). L’un de ses avantages est de permettre l’évolution de l’organisation des URL sans avoir à parcourir le code source du projet pour remplacer les URL obsolètes.
La première information dont nous disposons pour récupérer une URL est un moyen d’identifier la vue qui lui est associée (par exemple, son nom), les autres informations nécessaires à la recherche de la bonne URL sont les types (positionnel, nommé) et les valeurs des paramètres de la vue.
Django met à disposition une solution qui fait en sorte que le système d’abstraction d’URL est le seul à connaître l’organisation des URL. Vous lui donnez votre configuration d’URL et vous pouvez ensuite l’utiliser dans les deux sens :
À partir d’une URL demandée par un utilisateur/navigateur, il appelle la vue Django concernée en lui passant tous les paramètres dont elle a besoin en extrayant les valeurs telles quelles de l’URL.
À partir de l’identification de la vue Django concernée ainsi que les valeurs des paramètres qui lui seraient passés, il renvoie l’URL associée.
Le premier correspond à l’utilisation dont nous avons parlé dans les sections précédentes. Le second correspond à ce qu’on appelle la résolution inversée des URL, la correspondance inverse d’URL, la recherche inverse d’URL, ou plus simplement l’inversion d’URL.
Django met à disposition des outils pour réaliser une inversion d’URL dans les différentes couches où les URL sont nécessaires :
Dans les gabarits : en utilisant la balise de gabarit url.
Dans le code Python : en utilisant la fonction django.core.urlresolvers.reverse().
Dans le code plus abstrait amené à manipuler les URL des instances de modèle Django : la méthode get_absolute_url().
Reprenons comme exemple cette ligne de configuration d’URL :
from django.conf.urls import patterns, url
urlpatterns = patterns('',
#...
url(r'^articles/(\d{4})/$', 'news.views.year_archive'),
#...
)
D’après ce schéma, l’URL de l’archive correspondant à l’année nnnn est /articles/nnnn/.
Vous pouvez obtenir ces URL dans les gabarits en utilisant :
<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>
Ou en Python :
from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect
def redirect_to_year(request):
# ...
year = 2006
# ...
return HttpResponseRedirect(reverse('news.views.year_archive', args=(year,)))
Si, pour quelque raison que ce soit, vous décidez de changer l’URL à laquelle les archives annuelles d’articles sont sauvegardées, vous aurez seulement besoin de changer la ligne concernée dans la configuration d’URL.
Dans certains cas où les vues ont un comportement générique, une relation N:1 peut exister entre les URL et les vues. Dans ces cas-là, le nom de la vue n’est pas suffisant pour utiliser l’inversion d’URL. Lisez la section suivante pour découvrir la solution qu’apporte Django à ce problème.
Il est assez commun d’utiliser la même vue pour plusieurs motifs d’URL dans une configuration d’URL. Par exemple, ces deux motifs d’URL pointent tous les deux vers la vue archive:
from django.conf.urls import patterns, url
from mysite.views import archive
urlpatterns = patterns('',
url(r'^archive/(\d{4})/$', archive),
url(r'^archive-summary/(\d{4})/$', archive, {'summary': True}),
)
Ceci est tout à fait valable, mais est source de problèmes quand vous essayez d’utiliser l’inversion d’URL (via la fonction reverse() ou la balise de gabarit url). En conservant cet exemple, si vous vouliez récupérer l’URL associée à la vue archive, la résolution inverse d’URL de Django s’emmêlerait les pinceaux, car deux motifs d’URL pointent vers cette vue.
Pour résoudre ce problème, Django propose des motifs d’URL nommés. Cela signifie que vous pouvez donner un nom à un motif d’URL afin de le distinguer des autres motifs utilisant la même vue et les mêmes paramètres. Vous pouvez ensuite utiliser ce nom lors de l’inversion d’URL.
Voici l’exemple précédent, réécrit afin d’utiliser les motifs d’URL nommés
from django.conf.urls import patterns, url
from mysite.views import archive
urlpatterns = patterns('',
url(r'^archive/(\d{4})/$', archive, name="full-archive"),
url(r'^archive-summary/(\d{4})/$', archive, {'summary': True}, name="arch-summary"),
)
Une fois ces noms définis (full-archive et arch-summary), vous pouvez cibler individuellement chaque motif en utilisant son nom :
{% url 'arch-summary' 1945 %}
{% url 'full-archive' 2007 %}
Même si dans ce cas les deux motifs d’URL se réfèrent à la vue archive, l’emploi du paramètre name dans django.conf.urls.url() permet de les différencier au niveau des gabarits.
La chaîne utilisée pour les noms des URL peut contenir tous les caractères que vous voulez. Vous n’êtes pas tenus d’utiliser des noms Python valables.
Note
Lorsque vous nommez vos motifs d’URL, assurez-vous d’utiliser des noms qui ont peu de chances d’entrer en conflit avec ceux d’une autre application. Si vous appelez votre motif d’URL comment, et qu’une autre application fait la même chose, il n’y a aucune garantie que la bonne URL sera insérée dans votre gabarit quand vous utilisez ce nom.
L’ajout d’un préfixe à vos noms d’URL, peut-être dérivé du nom de l’application, permet de réduire les risques de collision. Nous recommandons quelque chose comme myapp-comment plutôt que comment.
Lorsque vous devez déployer plusieurs instances d’une même application, il peut être utile de pouvoir différencier les instances. C’est particulièrement important lorsqu’on utilise des motifs d’URL nommés, car plusieurs instances d’une même application partageront les URL nommées. Les espaces de noms sont une manière de différencier ces URL nommées.
Un espace de noms d’URL se compose de deux parties, qui sont les deux des chaînes de caractères :
Ceci définit le nom de l’application déployée. Chaque instance d’une même application possède le même espace de noms d’application. Par exemple, l’application d’administration de Django possède l’espace de noms d’application 'admin', comme l’on peut s’y attendre.
Ceci identifie une instance particulière d’une application. Les espaces de noms d’instance doivent être uniques dans tout un projet. Cependant, un espace de noms d’instance peut être identique à l’espace de noms d’application. C’est utilisé pour identifier l’instance par défaut d’une application. Par exemple, l’instance par défaut du site d’administration de Django possède l’espace de noms d’instance 'admin'.
Les URL avec espace de noms sont identifiées par l’opérateur ':'. Par exemple, la page d’accueil principale de l’application d’administration est référencée par 'admin:index'. Cela indique une URL nommée 'index' dans un espace de noms 'admin'.
Les espaces de noms peuvent aussi être imbriqués. L’URL nommée 'foo:bar:whiz' recherche un motif nommé 'whiz' dans l’espace de noms 'bar', lui-même défini à l’intérieur de l’espace de noms de premier niveau 'foo'.
Lorsqu’il reçoit une URL avec espace de noms à résoudre (par ex. 'myapp:index'), Django partage le nom complet en segments et effectue la recherche suivante :
Premièrement, Django cherche un espace de noms d’application correspondant (dans cet exemple, 'myapp'). Cela produira une liste d’instances de cette application.
Si une application courante est définie, Django trouve et renvoie le résolveur d’URL de cette instance. L’application courante peut être définie en tant qu’attribut du contexte de gabarit ; les applications qui s’attendent à être déployées plusieurs fois dans un projet devraient définir l’attribut current_app pour tout objet Context ou RequestContext utilisé pour faire le rendu d’un gabarit.
L’application courante peut également être indiquée manuellement comme paramètre de la fonction django.core.urlresolvers.reverse().
S’il n’existe pas d’application courante, Django cherche une instance d’application par défaut. Celle-ci correspond à l’instance dont l’espace de noms d’instance est identique à l’espace de noms d’application (dans cet exemple, une instance de myapp appelée 'myapp').
S’il n’existe pas d’instance d’application par défaut, Django sélectionne la dernière instance déployée de l’application, quel que soit son nom d’instance.
Si l’espace de noms indiqué ne correspond pas à un espace de noms d’application à l’étape 1, Django effectue une recherche directe d’espace de noms en tant qu’espace de noms d’instance.
S’il existe des espaces de noms imbriqués, ces étapes sont répétées pour chaque partie de l’espace de noms jusqu’à ce qu’il ne reste plus que le nom de la vue comme élément non résolu. Le nom de vue est ensuite résolu comme URL dans l’espace de noms qui a été trouvé.
Pour montrer ce processus de résolution en pratique, considérez un exemple contenant deux instances de myapp: une appelée 'foo' et l’autre 'bar'. myapp possède une page d’accueil principale dont l’URL est nommée 'index'. Avec cette configuration, les recherches suivantes sont possibles :
Si l’une des instances est courante, c’est-à-dire que nous construisons une page utilitaire dans l’instance 'bar', 'myapp:index' est résolu à la page d’accueil de l’instance 'bar'.
S’il n’existe pas d’instance courante, par exemple si nous produisons une page quelque part ailleurs sur le site, 'myapp:index' est résolu en rapport avec l’instance myapp inscrite en dernier. Comme il n’y a pas d’instance par défaut, c’est la dernière instance inscrite de myapp qui est utilisée. Cela pourrait être 'foo' ou 'bar' en fonction de l’ordre dans lequel ils apparaissent dans les motifs d’URL du projet.
'foo:index' est toujours résolu comme page d’accueil de l’instance 'foo'.
S’il existait aussi une instance par défaut, c’est-à-dire une instance nommée 'myapp', voici ce qui se produirait :
Si l’une des instances est courante, c’est-à-dire que nous construisons une page utilitaire dans l’instance 'bar', 'myapp:index' est résolu à la page d’accueil de l’instance 'bar'.
S’il n’existe pas d’instance courante, par exemple si nous produisons une page quelque part ailleurs sur le site, 'myapp:index' est résolu à la page d’accueil de l’instance par défaut.
Cette fois encore, 'foo:index' est résolu comme page d’accueil de l’instance 'foo'.
Les espaces de noms d’URL de configurations d’URL incluses peuvent être définis de deux manières.
Premièrement, vous pouvez fournir les espaces de noms application et instance comme paramètres à django.conf.urls.include() au moment de construire les motifs d’URL. Par exemple :
url(r'^help/', include('apps.help.urls', namespace='foo', app_name='bar')),
Ceci va inclure les URL définies dans apps.help.urls dans l’ espace de noms d’application 'bar' avec l’espace de noms d’instance 'foo'.
Deuxièmement, vous pouvez inclure un objet contenant des données d’espace de noms intégrées. Si vous incluez par include() un objet tel que renvoyé par patterns(), les URL contenues dans cet objet seront ajoutées à l’espace de noms global. Cependant, vous pouvez aussi inclure un tuple de 3 éléments contenant :
(<patterns object>, <application namespace>, <instance namespace>)
Par exemple :
from django.conf.urls import include, patterns, url
help_patterns = patterns('',
url(r'^basic/$', 'apps.help.views.views.basic'),
url(r'^advanced/$', 'apps.help.views.views.advanced'),
)
url(r'^help/', include((help_patterns, 'bar', 'foo'))),
Cela va inclure les motifs d’URL nommés dans les espaces de noms d’application et d’instance donnés.
Par exemple, le site d’administration de Django est déployé par des instances de AdminSite. Les objets AdminSite possèdent un attribut urls: un tuple de 3 éléments contenant tous les motifs du site d’administration correspondant, en plus de l’espace de noms d’application 'admin' ainsi que le nom de l’instance d’administration. C’est cet attribut urls que vous incluez dans les motifs urlpatterns de votre projet lorsque vous déployez une instance du site d’administration.
Prenez garde de bien passer un tuple à include(). Si vous transmettez simplement trois paramètres, include(help_patterns, 'bar', 'foo'), Django ne génère pas d’erreur, mais en fonction de la signature de include(), 'bar' correspondra à l’espace de noms d’instance et 'foo' à l’espace de noms d’application, au lieu de l’inverse.
Jan 13, 2016