Django

Url

1.  URL Dispatcher

Web ovládaný Djangem je propojený odkazy ve formě URL. Každé takové URL ukazuje na konkrétní funkci a pod. ve vrstvě view. Tyto vazby jsou nastaveny v souboru urls.py, který je v adresáři projektu vedle settings.py, kde je zase parametr ROOT_URLCONF, který ve výchozím nastavení ukazuje právě na tento urls.py (což znamená, že si to můžeme změnit).

1.1  Úvodní příklad

Příklad souboru urls.py:

 from django.conf.urls import patters, url, include

 urlpatterns = patterns('',
        url(r'^$', 'myapp.views.index'),
        url(r'^contacts/$', 'myapp.views.contacts'),
        url(r'^contacts/(\d)/, 'myapp.views.contacts_show'),
        )

 urlpatterns += patterns('objednavky.views',
        url(r'^objednavky/$', 'objednavky'),
        url(r'^objednavky/firma/(\d)/', 'obj_firma'),
        url(r'^objednavky/fakt/(?P<firma>\d)/(?P<rok>\d{4}), 'obj_firma_prehled'),
        )

 urlpatterns += patterns('',
        url(r'^sklad/$', include('sklady.urls')),
        )

Vše potřebné má Django v modulu django.conf.urls. Vytvoříme proměnnou urlpatterns pomocí funkce Djanga patterns(). Ta bere jako argument n-tici. První položka v n-tici je tzv. prefix (viz dále). Následují položky vytvořené funkcí url(), jejíž prvním argumentem je regulární výraz popisující url adresu. Druhým argumentem je název funkce, která se má spustit.

Django prohledává n-tici postupně. Jakmile narazí na shodu url, zavolá funkci z druhého parametru a předá jí argumenty. Prvním argumentem je vždy request (slovník obsahující obvyklé http náležitosti jako jsou GET, POST, session,...). Ten je vkládán automaticky Djangem. Další argumenty mohou být přímo v url. Je možné přidat i další paramter slovník s dalšími proměnnými, které budou příslušené view funkci předány.

Pro přehlednost můžeme urlpatterns plnit postupně a oddělit tak od sebe logické části našeho webu. Jak je to v ukázce výše.

V první části

 urlpatterns = patterns('',
        url(r'^$', 'myapp.views.index'),
        url(r'^contacts/$', 'myapp.views.contacts'),
        url(r'^contacts/(\d)/, 'myapp.views.contacts_show'),
        )

je prefix prázdný. Následuje řádek s prázdným regulárním výrazem - odkazuje na kořenovou adresu webu (http://www.example.com/) - tedy, kde za lomítkem už nic nenásleduje. V tomto případě se bude volat funkce index(request) ze souboru views.py, který je v adresáři aplikace myapp.

Druhý řádek obsahuje url contacts/ a zavolá se funkce contacts. Znak $ v regulárním výrazu znamená, že url musí obsahovat právě jen ono contacts/ a už nic za tím.

Třetímu řádku bude odpovídat např http://www.example.com/contacts/249/. Všimněme si, že znak $ schází, takže tento řádek spolkne i všechno, co by za číslem a lomítkem ještě pokračovalo dále. Tento "ocásek" by byl ale ignorován.

U tohoto řádku bude volána funkce contacts_show a kromě parametru request jí bude předán i poziční parametr s číslem (zde 249). Příslušná funkce ve view.py by vypadala takto:

 def contacts_show(request, partner_no):

Číslo by bylo předáno do proměnné partner_no.

  • Čísla jsou předávána vždy jako typ string. O konverzi na int, float apod se musíme postarat sami.
  • Parametry v url musí být v kulatých závorkách, aby je Django poznalo.
  • Úvodní lomítko z URL do vzoru nepíšeme - je automatické, protože je v URL vždy. (r'neco/', nikoliv r'/neco/').

Ve druhé části příkladu

 urlpatterns += patterns('objednavky.views',
        url(r'^objednavky/$', 'objednavky'),
        url(r'^objednavky/firma/(\d)/', 'obj_firma'),
        url(r'^objednavky/fakt/(?P<firma>\d)/(?P<rok>\d{4}), 'obj_firma_prehled'),
        )

Vidíme, že prefix není prázdný, ale obsahuje objednavky.views. To je vlastně jakési vytknutí před závorku, které jsme mohli využít už v první části příkladu, kdybycho do prefixu dali myapp.views. Pak bychom odkazy na fuknce mohli zkrátit právě o tento prefix na index, contacts a contacts_show. V druhé části tedy prefix ukazuje na soubor views.py z aplikace objednavky). Pokud bychom prefix nevyužili, volali bychom v prvním řádku objednavky.views.objednavky místo pouhého objednavky.

Všimněme si, že ve druhém řádku předáváme opět číselný parametr.

Ve třetím řádku rovněž předáváme parametry, tentokrát ale ne pozičním stylem, ale jako klíčové argumenty (pojmenované). U posledního paramteru rok je ve složených závorkách uvedena požadovaná délka čísla. Příslušná funkce ve views.py by byla například takováto:

 def obj_firma_prehled(request, firma="1", rok="2000"):

Pro jistotu můžeme vkládat defaultní hodnoty.

Třetí část příkladu

 urlpatterns += patterns('',
        url(r'^sklad/', include('sklady.urls')),
        )

ukazuje možnost, že aplikace sklady má svůj vlastní soubor urls.py, který je sem, do hlavního urlpatterns vložen právě funkcí include(). Regulární výraz zde nesmí být zakončen znakem $, protože vložený sklady.urls.py bude dostávat všechna url začínající sklad/.

Naproti tomu ve vkládaném sklady.urls.py už jednotlivé regulární výrazy nebudou na začátku obsahovat ono sklady/. Takže soubor urls.py v aplikaci sklady bude obsahovat například

 urlpatterns = patterns('',
        url('^prehled/$), sklady.views.prehled),
        )

což ve skutečnosti přestavuje URL.

  http://www.example.com/sklady/prehled/

Podotkněme, že kdyby "kořenové" url odchytávalo i nějakou proměnnou, byla by předána do vloženého url tak, jak je očekáváno:

 # In settings/urls/main.py
 urlpatterns = patterns('',
     url(r'^(?P<username>\w+)/blog/', include('foo.urls.blog')),
 )

 # In foo/urls/blog.py
 urlpatterns = patterns('foo.views',
     url(r'^$', 'blog.index'),
     url(r'^archive/$', 'blog.archive'),
 )

Obsah proměnné username bude předán dál přes include().

1.2  Jak Django zpracovává request

Postup:

  1. Django zjistí, který URLConf modul použít (viz settings.py, parametru ROOT_URLCONF).
  2. V nalezeném modulu hledá proměnnou urlpatterns vytvořenou funkcí Djanga patterns.
  3. Prohledává postupně vzory regulárních výrazů a zastaví na první shodě.
  4. Zavolá funkci z druhého parametru (z příslušeného views.py) a předá jí parametr request (přesněji HttpRequest) a případné další hodnoty jako další poziční či klíčové parametry.
  5. Nenajde-li se žádný odpovídající vzor, nebo nastane výjimka, Django zavolá příslušné view pro obsluhu chyby.

1.3  Další pravidla

Django při zpracování URL ignoruje doménovou část i GET a POST a pod. V URL

 http://www.example.com/sklady/

bude hledat sklady/. V URL

 http://www.example.com/sklady/?sklad=6

bude opět hledat jen sklady/.

1.4  Předání extra parametrů

Funkce url() má volitelný třetí parametr, který je slovník přes nějž můžeme předat další parametry:

 urlpatterns = patterns('blog.views',
     url(r'^blog/(?P<year>\d{4})/$', 'year_archive', {'foo': 'bar'}),
 )

Extra parametry lze předat i při použití funkce include(). Parametry pak budou předány všem řádkům ve volaném url.py.

1.5  Callable místo řetězce s cestou

Místo dosud používaného stylu

 urlpatterns = patterns('',
     url(r'^archive/$', 'mysite.views.archive'),
     url(r'^about/$', 'mysite.views.about'),
     url(r'^contact/$', 'mysite.views.contact'),
 )

lze postupovat i takto:

 from mysite import views

 urlpatterns = patterns('',
     url(r'^archive/$', views.archive),
     url(r'^about/$', views.about),
     url(r'^contact/$', views.contact),
 )

1.6  Class based views

Django nabízí generická views, která nejsou funkcí, ale třídou. Pokud taková view používáme, musíme je do url.py vždy importovat:

 from mysite.views import ClassBasedView

 urlpatterns = patterns('',
     url(r'^myview/$', ClassBasedView.as_view()),
 )

1.7  Reverzní vyhodnocování URL

V aplikaci nemusí být pohodlné používat přímo URL. Tak by se aplikace stala natvrdo zadrátovanou a případná změna v url.py by znamenala hledání odpovídající URL ve všech souborech aplikace a jejich opravování. Místo toho lze použít reverzní vyhodnocení, kdy se uvádí odkaz na příslušnou funkci ve view a Django z tohoto odkazu vygeneruje příslušné URL.

Příklad urls.py:

from django.conf.urls import patterns, url

urlpatterns = patterns('',

    #...
    url(r'^articles/(\d{4})/$', 'news.views.year_archive'),
    #...

)

V šabloně můžeme potom napsat:

 {% load url %}

 <a href="{% url 'news.views.year_archive' year %}">{{ year }} Archive</a>

year je normální promměná, kterou dostane šablona z view, které jí volalo. Obsah href="..." Django převede na normální URL dle vzoru v urls.py.

V kódu Pythonu (např ve view):

 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,)))

Pojmenování vzorů URL

V některých případech nemusí být dobře použitelné odkazovat se v reverzních voláních na jméno view funkce. Pak můžeme využít možnosti Djanga si takový odkaz pojmenovat:

 urlpatterns = patterns('',
     url(r'^archive/(\d{4})/$', archive, name="full-archive"),
     url(r'^archive-summary/(\d{4})/$', archive, {'summary': True}, name="arch-summary"),
 )

V šablonách a kódu Pythonu se pak odkazujeme na tato jména daná parametrem name. Př:

 {% url 'arch-summary' 1945 %}

1.8  Jmenné prostory

Slouží k přehlednější identifikaci odkazů v projektech, které jsou modulární a obsahují více aplikací než je malé množství. Pak při vkládání urls aplikace pomocí include můžeme přiřadit jmenný prostor aplikaci i instanci:

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

Někdy je toto řešení nutné, aby se vyřešil problém s konfiltem jmen.

Další příklad z dokumentace se týká embedded dat:

 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')),

Tady ale praktické využití tak docela nevidím, takže neposloužím.