URL Verwaltung
Diese Dokumentation gilt für Djangos Entwicklerversion, die zum Teil erhebliche Unterschiede zur letzten veröffentlichen Version aufweist.
Ein fehlerfreies, elegantes URL Schema ist wichtiger Bestandteil einer hochwertigen Webanwendung. Django erlaubt dir, deine URLs frei zu gestalten ohne vom Framework eingeschränkt zu werden.
Es werden keinerlei Endungen, wie .php oder .cgi verwendet und auch selbstverständlich nicht dieser ganze 0,2097,1-1-1928-00 Unsinn.
Siehe Cool URIs don’t change, geschrieben vom WWW-Erfinder Tim Berners-Lee, um Argumente zu finden, warum URLs fehlerfrei und benutzbar sein sollten.
Übersicht
Um URLs für eine Anwendung zu gestalten, wird ein Python-Modul, informell URLconf (URL Konfiguration) genannt, angelegt. Dieses Modul wird in reinem Python Code geschrieben und enthält eine einfache Zuordnung von URL-Mustern (einfache reguläre Ausdrücke) zu Python Funktionen (deine Views).
Diese Zuordnung kann je nach Bedarf kurz oder lang ausfallen, und andere Zuordnungen können von dort aus referenziert werden. Und, da es reiner Python Code ist, können URLs dynamisch gestaltet werden.
Wie Django einen Request abarbeitet
Der folgende Algorithmus beschreibt den Prozess, den das System durchläuft, um zu bestimmen, welcher Python Code ausgeführt wird, nachdem ein Benutzer eine Seite von einer Website anfordert, die mit Django betrieben wird:
- Django schaut zuerst nach der ROOT_URLCONF-Einstellung in deiner Einstellungs-Datei. Diese sollte ein String sein, der den vollen Python Import-Pfad zur URLconf enthält. Beispielsweise: "mydjangoapps.urls".
- Django lädt dieses Python-Modul und schaut nach der Variablen urlpatterns. Der Wert dieser Variable sollte eine Python-Liste im Format, wie sie von der Funktion django.conf.urls.defaults.patterns() zurückgegeben wird, sein.
- Django durchläuft nun jedes URL-Muster nacheinander und stoppt beim ersten Treffer der angeforderten URL.
- Sobald einer der regulären Ausdrücke zutrifft, importiert Django die zugeordnete View und ruft diese auf. Diese ist nur eine einfache Python-Funktion. Die View bekommt ein Request-Objekt als erstes Argument und jeden weiteren Wert, der vom regulären Ausdruck erfasst wurde, als zusätzliches Argument übergeben.
Beispiel
Hier eine Beispiel-URLconf:
from django.conf.urls.defaults import *
urlpatterns = patterns('',
(r'^articles/2003/$', 'news.views.special_case_2003'),
(r'^articles/(\d{4})/$', 'news.views.year_archive'),
(r'^articles/(\d{4})/(\d{2})/$', 'news.views.month_archive'),
(r'^articles/(\d{4})/(\d{2})/(\d+)/$', 'news.views.article_detail'),
)
Bemerkungen:
- from django.conf.urls.defaults import * stellt die patterns()-Funktion zur Verfügung.
- Zum Erfassen eines Wertes aus einer URL muss man um diesen nur Klammern setzen.
- Es wird kein führender Schrägstrich erwartet, da jede URL diesen beinhaltet. Zum Beispiel heisst es ^articles und nicht ^/articles.
- Das 'r' vor jedem regulären Ausdruck ist zwar optional, allerdings empfohlen. Es teilt Python mit, dass ein String “raw” ist — dass nichts im String ersetzt wird. Siehe Erklärung in Dive Into Python.
Request Beispiele:
- Ein Request auf /articles/2005/03/ würde auf den dritten Eintrag in der Liste zutreffen. Django würde dann die Funktion news.views.month_archive(request, '2005', '03') aufrufen.
- /articles/2005/3/ würde zu keinem URL-Muster zutreffen, da der dritte Eintrag in der Liste den Monat zweistellig erwartet.
- /articles/2003/ würde beim ersten Muster in der Liste und nicht dem zweiten zutreffen, da die Muster in Reihe abgearbeitet werden und da das erste Muster zuerst getestet wird. Tu dir keien Zwang an und verändere die Sortierung, um Spezialfälle, wie diesen, zu untersuchen.
- /articles/2003 würde keinen passenden Treffer liefern, da jedes Muster verlangt, dass die URL mit einem Schrägstrich endet.
- /articles/2003/03/3/ würde beim letzten Muster zutreffen. Django würde die Funktion news.views.article_detail(request, '2003', '03', '3') aufrufen.
Benannte Gruppen
Die obigen Beispiele verwendeten einfache, reguläre Ausdrücke mit nicht-benannten Gruppen (mittels Klammerung), um Teile der URL zu erfassen und diese als positionsbezogene Argumente an eine View zu übergeben. Für erweiterte Anwendung kann man auch benannte Gruppen in regulären Ausdrücken verwenden, um Teile einer URL zu erfassen und diese als Schlagwort-Argument an eine View zu übergeben.
Die Syntax für benannte Gruppen in regulären Ausdrücken für Python lautet (?P<name>pattern), wobei name den Namen der Gruppe und pattern das zu treffende Muster beschreibt.
Hier die obige Beispiel-URLconf mit benannten Gruppen:
urlpatterns = patterns('',
(r'^articles/2003/$', 'news.views.special_case_2003'),
(r'^articles/(?P<year>\d{4})/$', 'news.views.year_archive'),
(r'^articles/(?P<year>\d{4})/(?P<month>\d{2})/$', 'news.views.month_archive'),
(r'^articles/(?P<year>\d{4})/(?P<month>\d{2})/(?P<day>\d+)/$', 'news.views.article_detail'),
)
Dies erfüllt denselben Zweck, wie das vorige Beispiel, allerdings mit dem folgendem Unterschied: Die erfassten Werte werden als Schlagwort-Argumente und nicht als positionsbezogene an die View-Funktionen übergeben. Zum Beispiel:
- Ein Request auf /articles/2005/03/ würde die Funktion news.views.month_archive(request, year='2005', month='03'), anstatt news.views.month_archive(request, '2005', '03') aufrufen.
- Ein Request auf /articles/2003/03/3/ würde die Funktion news.views.article_detail(request, year='2003', month='03', day='3') aufrufen.
In der Praxis bedeutet dies, dass deine URLconfs ein wenig eindeutiger und weniger fehleranfällig für Argument-Anordungs-Bugs sind — und zusätzlich kannst du die Argumente in deinen View-Funktionsdefinitionen umsortieren. Natürlich geht das nur auf Kosten der Kürze; viele Entwickler empfinden die benannte-Gruppen-Syntax als hässlich und zu ausführlich.
Der Algorithmus, um Muster/Gruppen zu treffen
In diesem Abschnitt wird der Algorithmus erklärt, wie der URLconf-Parser unter Berücksichtigung von benannten im Vergleich zu nicht-benannten Gruppen arbeitet:
Falls irgendwelche benannten Argumente vorhanden sind, werden diese verwendet und es werden nicht-benannte Argumente ignoriert.
In beiden Fällen werden allerdings alle zusätzlichen Schlagwort-Argumente als Schlagwort-Argumente mit übergeben. Siehe “Übergebe zusätzliche Optionen an View-Funktionen” weiter unten.
Nach was sucht die URLconf
In der URLconf wird nach der angeforderten URL gesucht, der als normaler Python-String interpretiert wird. Dieser enthält keinerlei GET- oder POST-Parameter und auch keinen Domain-Namen.
Beispielsweise würde die URLconf bei einem Request auf http://www.example.com/myapp/ nach /myapp/ suchen.
Bei einem Request auf http://www.example.com/myapp/?page=3 würde die URLconf nach /myapp/ suchen.
Die URLconf betrachtet die Request-Methode nicht. In anderen Worten: jede Request-Methode — POST, GET, HEAD, usw. — zeigt auf die gleiche Funktion für jede gleiche URL.
Syntax der urlpatterns Variable
urlpatterns sollte eine Python-Liste im Format, wie sie von der Funktion django.conf.urls.defaults.patterns() zurückgegeben wird, sein. Verwende stets patterns(), um die urlpatterns-Variable zu erstellen.
Als Grundsatz gilt, from django.conf.urls.defaults import * am Anfang der URLconf zu setzen. Das ermöglicht deinem Modul dann den Zugriff auf die folgenden Objekte:
patterns
Eine Funktion, die einen Präfix und eine beliebige Anzahl an URL-Mustern akzeptiert, und die eine Liste von URL-Mustern, wie sie von Django benötigt wird, zurückgibt.
Das erste Argument von patterns() ist ein String Namens prefix. Siehe “Der View Präfix” weiter unten.
Die weiteren Argumente sind Tupel in folgendem Format:
(Regulärer Ausdruck, Python-Funktion [, optionales Dictionary [, optionaler Name]])
…wobei optionales Dictionary und optionaler Name optional sind. (Siehe Übergebe zusätzliche Optionen an View-Funktionen weiter unten.)
url
Neu in Djangos Entwicklerversion
Du kannst die url()-Funktion anstatt eines Tupels verwenden, um diese als Argument an patterns() zu übergeben. Das ist praktischer, wenn du einen Namen ohne zusätzliches Dictionary-Argument festlegen willst. Zum Beispiel:
urlpatterns = patterns('',
url(r'/index/$', index_view, name="main-view"),
...
)
Diese Funktion hat fünf Argumente, wobei die meisten optional sind:
url(regex, view, kwargs=None, name=None, prefix='')
Siehe Namensgebung von URL-Mustern, um näheres darüber zu erfahren, für was der name-Parameter verwendet wird.
Der prefix-Parameter hat die selbe Bedeutung wie das erste Argument von patterns() und ist lediglich wichtig, wenn ein String als view-Parameter übergeben wird.
handler404
Ein String, der den vollständigen Python-Import-Pfad zur View enthält, die aufgerufen werden soll, wenn keines der URL-Muster zutrifft.
Die Voreinstellung ist 'django.views.defaults.page_not_found'. Dieser Standardwert sollte für die meisten Fälle ausreichen.
handler500
Ein String, der den vollständigen Python-Import-Pfad zur View enthält, die aufgerufen werden soll, wenn ein Server-Fehler auftritt. Server-Fehler treten auf, wenn Laufzeitfehler im View-Code auftreten.
Die Voreinstellung ist 'django.views.defaults.server_error'. Dieser Standardwert sollte für die meisten Fälle ausreichen.
include
Eine Funktion, die einen vollständigen Python-Import-Pfad zu einer anderen URLconf als Parmeter annimmt, die an dieser Stelle eingebunden werden soll. Siehe Andere URLconfs einbinden weiter unten.
Bemerkungen zur Texterfassung innerhalb URLs
Jedes erfasste Argument wird stets als einfacher Python String an die View übergeben, ohne Rücksicht darauf zu nehmen, von welchem Typ der Treffer des regulären Ausdrucks ist. Zum Beispiel wird in der URLconf-Zeile:
(r'^articles/(?P<year>\d{4})/$', 'news.views.year_archive'),
…das year-Argument als String und nicht als Integer an news.views.year_archive() übergeben, obwohl \d{4} nur für Integer-Strings zutrifft. Hier eine Beispiel-URLconf und zugehörige View:
# URLconf
urlpatterns = patterns('',
(r'^blog/$', 'blog.views.page'),
(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.
Im obigen Beispiel zeigen beide URL-Muster auf dieselbe View — blog.view.page — allerdings erfasst das erste Muster kein Argument von der URL. Wenn das erste Muster trifft, wird die page()-Funktion den Standardwert "1" für das num-Argument verwenden. Sobald das zweite Muster trifft, wird page() für num den Wert annehmen, der vom regulären Ausdruck erfasst wurde.
Performance
Jeder reguläre Ausdruck in der Variablen urlpatterns wird kompiliert, sobald er das erste Mal aufgerufen wird. Dadurch wird das System verflucht schnell.
Der View Präfix
Du kannst einen gemeinsamen Präfix in deinem patterns()-Aufruf setzen, um Code-Wiederholungen zu reduzieren.
Hier die Beispiel-URLconf der Django Übersicht:
from django.conf.urls.defaults import *
urlpatterns = patterns('',
(r'^articles/(\d{4})/$', 'mysite.news.views.year_archive'),
(r'^articles/(\d{4})/(\d{2})/$', 'mysite.news.views.month_archive'),
(r'^articles/(\d{4})/(\d{2})/(\d+)/$', 'mysite.news.views.article_detail'),
)
In diesem Beispiel hat jede View einen gemeinsamen Präfix — 'mysite.news.views'. Anstatt das jedes Mal für jeden Eintrag in urlpatterns erneut zu tippen, kannst du das erste Argument der patterns()-Funktion verwenden, um einen Präfix zu setzen, der vor jeder View-Funktion angefügt wird.
Mit diesem Wissen kann das oben genannte Beispiel deutlich kürzer formuliert werden:
from django.conf.urls.defaults import *
urlpatterns = patterns('mysite.news.views',
(r'^articles/(\d{4})/$', 'year_archive'),
(r'^articles/(\d{4})/(\d{2})/$', 'month_archive'),
(r'^articles/(\d{4})/(\d{2})/(\d+)/$', 'article_detail'),
)
Beachte, dass dem Präfix kein Punkt angehängt wird ("."). Django fügt das selbst hinzu.
Mehrere View Präfixe
In der Praxis stößt du bestimmt bald darauf, dass die Views in deiner urlpatterns-Variable nicht immer den selben Präfix haben. Dennoch kannst du den Vorteil der Abkürzung mit Hilfe des View Präfixes nutzen, um Wiederholungen zu entfernen. Du musst lediglich mehrere patterns()-Objekte folgendermassen zusammenfügen:
Alt:
from django.conf.urls.defaults import *
urlpatterns = patterns('',
(r'^$', 'django.views.generic.date_based.archive_index'),
(r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/$', 'django.views.generic.date_based.archive_month'),
(r'^day/(?P<day>\w+)/$', 'weblog.views.day'),
)
Neu:
from django.conf.urls.defaults import *
urlpatterns = patterns('django.views.generic.date_based',
(r'^$', 'archive_index'),
(r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/$','archive_month'),
)
urlpatterns += patterns('weblog.views',
(r'^day/(?P<day>\w+)/$', 'day'),
)
Andere URLconfs einbinden
An jeder Stelle deiner urlpatterns können andere URLconf-Module eingebunden werden. Dadurch ordnet man im Wesentlichen eine Menge von URLs unterhalb von anderen an.
Hier ein Beispiel anhand der URLconf der Django-Website. Diese bindet einige andere URLconfs ein:
from django.conf.urls.defaults import *
urlpatterns = patterns('',
(r'^weblog/', include('django_website.apps.blog.urls.blog')),
(r'^documentation/', include('django_website.apps.docs.urls.docs')),
(r'^comments/', include('django.contrib.comments.urls.comments')),
)
Beachte, dass die regulären Ausdrücke in diesem Beispiel nicht mit $ (Markierung für String-Ende), sondern mit einem Schrägstrich enden. Sobald Django auf include() stösst, wird von der URL der Treffer abgeschnitten und der übrige Rest an die eingebundene URLconf zur Verarbeitung weitergereicht.
Erfasste Parameter
Eine eingebundene URLconf bekommt alle erfassten Parameter von den Eltern-URLconfs, sodass folgendes Beispiel gültig ist:
# In settings/urls/main.py
urlpatterns = patterns('',
(r'^(?P<username>\w+)/blog/', include('foo.urls.blog')),
)
# In foo/urls/blog.py
urlpatterns = patterns('foo.views',
(r'^$', 'blog.index'),
(r'^archive/$', 'blog.archive'),
)
Im obigen Beispiel wird die erfasste "username"-Variable an die eingebundene URLConf, wie erwartet, weitergegeben.
Übergebe zusätzliche Optionen an View-Funktionen
URLconfs besitzen die Funktionalität, zusätzliche Argumente, als Python-Dictionary, an deine View-Funktionen zu übergeben.
Jedes URLconf-Tupel kann ein drittes optionales Element erhalten, welches ein Dictionary mit zusätzlichen Schlagwörtern sein muss, das dann an die View-Funktion weitergeben wird.
Zum Beispiel:
urlpatterns = patterns('blog.views',
(r'^blog/(?P<year>\d{4})/$', 'year_archive', {'foo': 'bar'}),
)
In diesem Beispiel wird Django bei einem Request auf /blog/2005/ die View blog.views.year_archive() mit den folgenden Schlagwort-Argumenten aufrufen:
year='2005', foo='bar'
Diese Technik findet Verwendung bei Generic Views und im Syndication Framework, indem Metadaten und Optionen an Views übergeben werden.
Umgang mit Konflikten
Es ist möglich ein URL-Muster zu erstellen, das benannte Argumente erfasst und gleichzeitig die gleichen Namen in dessen Zusatzargumente-Dictionary zu setzen. Falls das passiert, werden die Argumente des Dictionary anstatt der erfassten Argumente verwendet.
Zusätzliche Parameter an include() übergeben
In gleicher Weise kann man zusätzliche Optionen an include() übergeben. Falls zusätzliche Optionen an include() übergeben werden, werden jeder Zeile der eingebundenen URLconf diese Zusatzparameter übergeben.
Zum Beispiel sind diese beiden URLconf-Blöcke identisch:
Erster Block:
# main.py
urlpatterns = patterns('',
(r'^blog/', include('inner'), {'blogid': 3}),
)
# inner.py
urlpatterns = patterns('',
(r'^archive/$', 'mysite.views.archive'),
(r'^about/$', 'mysite.views.about'),
)
Zweiter Block:
# main.py
urlpatterns = patterns('',
(r'^blog/', include('inner')),
)
# inner.py
urlpatterns = patterns('',
(r'^archive/$', 'mysite.views.archive', {'blogid': 3}),
(r'^about/$', 'mysite.views.about', {'blogid': 3}),
)
Beachte, dass Zusatzoptionen immer jeder Zeile in der eingebundenen URLconf weitergegeben werden, egal ob die View dieser Zeile in dessen aktuellen Konfiguration diese Optionen als gültigen Wert annimmt oder nicht. Aus diesem Grund sollte diese Technik nur angewandt werden, wenn du dir sicher bist, dass jede View der eingebundenen URLconf diese zusätzlichen Optionen akzeptiert, die du übergibst.
Übergabe von aufrufbaren Objekten anstatt Strings
Einige Entwickler empfinden es als natürlicher, dass man eher das aktuelle Python-Funktionsobjekt anstatt eines Strings, der den Pfad zu dem Modul enthält, übergibt. Diese Alternative wird unterstützt — du kannst jedes aufrufbare Objekt als View übergeben.
Ein Beispiel einer URLconf, welche die “String”-Notation verwendet:
urlpatterns = patterns('',
(r'^archive/$', 'mysite.views.archive'),
(r'^about/$', 'mysite.views.about'),
(r'^contact/$', 'mysite.views.contact'),
)
Man erreicht das gleiche Ergebnis, wenn man Objekte anstatt Strings übergibt. Du musst nur sicherstellen, dass die Objekte vorher importiert wurden:
from mysite.views import archive, about, contact
urlpatterns = patterns('',
(r'^archive/$', archive),
(r'^about/$', about),
(r'^contact/$', contact),
)
Das folgende Beispiel ist funktional identisch. Es ist nur ein wenig kompakter, da es das Modul importiert, das die einzelnen Views enthält, anstatt jede einzelne View separat zu importieren:
from mysite import views
urlpatterns = patterns('',
(r'^archive/$', views.archive),
(r'^about/$', views.about),
(r'^contact/$', views.contact),
)
Du entscheidest darüber, wie es dir am liebsten ist.
Allerdings kann bei Verwendung dieser Technik — Übergabe von Objekten anstatt Strings — die View-Präfix-Funktionalität (wie bereits in “Der View Präfix” beschrieben) nicht mehr verwendet werden.
Namensgebung von URL-Mustern
Neu in Djangos Entwicklerversion
Es ist üblich, dass die gleiche View-Funktion für mehrere URL-Muster innerhalb der URLconf verwendet wird. Zum Beispiel zeigen diese beiden URL-Muster auf die gleiche archive-View:
urlpatterns = patterns('',
(r'/archive/(\d{4})/$', archive),
(r'/archive-summary/(\d{4})/$', archive, {'summary': True}),
)
Das ist völlig zulässig, führt aber zu Problemen, wenn man versucht die URL rückwärts aufzulösen (unter Verwendung des permalink()-Decorators oder des {% url %} Template Tags). Um nun, anhand diese Beispiels, die URL archive-View per Rückwärtsauflösung zu erhalten, würde Django durcheinander kommen, da zwei URL-Muster auf die gleiche View zeigen.
Um dieses Problem zu lösen, unterstützt Django benannte URL-Muster. Das bedeutet, dass man jedem URL-Muster einen Namen geben kann, um dieses von anderen Mustern, die die gleiche View und Parameter verwenden, zu unterscheiden. Du kannst diesen Namen dann bei der Rückwärtsauflösung dieser URLs verwenden.
Hier das obige Beispiel unter Verwendung benannter URL-Muster:
urlpatterns = patterns('',
url(r'/archive/(\d{4})/$', archive, name="full-archive"),
url(r'/archive-summary/(\d{4})/$', archive, {'summary': True}, "arch-summary"),
)
Mit diesen beiden Namen (full-archive und arch-summary) kann man nun beide URL-Muster einzeln abrufen, indem man deren Namen verwendet:
{% url arch-summary 1945 %}
{% url full-archive 2007 %}
Selbst wenn beide URL-Muster auf die archive-View verweisen, können sie anhand des name-Parameters von url() im Template auseinander gehalten werden.
Der String, der für den URL-Namen verwendet wird, kann jegliches Zeichen enthalten. Man ist nicht auf gültige Python-Namen beschränkt.
Bemerkung
Bei Bennenung deiner URL-Muster solltest du darauf achten, dass diese sich nicht mit Mustern-Namen anderer Anwendungen überschneiden. Wenn du dein URL-Muster comment nennst und das auch in einer anderen Anwendung verwendet wird, gibt es keine Garantie dafür, welche URL, bei Verwendung dieses Namens, in deinem Template eingefügt wird.
Bei Verwendung eines Präfixes für den URL-Namen (vielleicht etwas, dass sich vom Anwendungsnamen ableitet) kann man die Möglichkeiten einer Kollision verringern. Wir empfehlen so etwas wie myapp-comment anstatt nur comment zu verwenden.
Hilfsmethoden
reverse()
Falls du etwas ähnliches wie {% url %} Template Tags innerhalb deines Codes verwenden möchtest, kannst du die Django-Funktion django.core.urlresolvers.reverse() aufrufen. Die reverse()-Funktion hat die folgende Signatur:
reverse(viewname, urlconf=None, args=None, kwargs=None)
viewname ist entweder der Funktionsname (entweder eine Funktionsreferenz oder ein String, der den Namen enthält, falls diese Form in urlpatterns verwendet wurde) oder der URL-Muster-Name. Gewöhnlich muss man sich keine Gedanken über die urlconf-Parameter machen, sondern lediglich die positions- sowie stichwortbezogenen Argumente übergeben. Zum Beispiel:
from django.core.urlresolvers import reverse
def myview(request):
return HttpResponseRedirect(reverse('arch-summary', args=[1945]))
permalink()
Der permalink()-Decorator ist zum Schreiben von kurzen Methoden, die einen vollständigen URL-Pfad zurückliefern, sinnvoll. Das wird zum Beispiel bei der get_absolute_url()-Methode der Modelle verwendet. Es sei hier für weitere Information über permalink() auf die Modell API Dokumentation hingewiesen.