Django, 2. cvičení url, views, templates. Úvod Views v djangu jsou funkce (definovány ve views.py souboru aplikace, na které jsou mapovány URL adresy. Jejich úkolem je vrátit odpověď na HTTP požadavek. Šablony slouží pro oddělení a usnadnění prezentace dat. Konktrétní URL adresa je mapována na konkrétní view funkci, která používá obvykle vlastní specifickou šablonu. Šablona je soubor obsahující obvykle směs html tagů a speciálních tagů určujících, která data se mají na daném místě renderovat. Nastavení url mapování na views: v souboru settings.py je přednastaveno následující: ROOT_URLCONF = 'drivers.urls' Toto nastavení říká, v jakém souboru hledat konfiguraci url. V tomto případě: drivers/urls.py (v djangu se tomu říká URLConf. V tomto souboru už jsme v minulém cvičení povolovali url pro administrátorskou aplikaci urlpatterns v drivers.urls vypadá nyní takto: # Example: # (r'^drivers_demo1/', include('drivers_demo1.foo.urls', # (r'^admin/doc/', include('django.contrib.admindocs.urls', (r'^admin/', include(admin.site.urls, Přidáme mapování na naše pohledy: # Example: # (r'^drivers/', include('drivers.foo.urls', # (r'^admin/doc/', include('django.contrib.admindocs.urls', (r'^stations/hello', 'stations.views.hello', (r'^stations/company/(?p<company_id>\d+', 'stations.views.company_name', (r'^stations/company_index', 'stations.views.company_index', (r'^stations/station_index', 'stations.views.station_index', (r'^stations/station/search', 'stations.views.capacity_search', (r'^stations/station/(?p<station_id>\d+/edit', 'stations.views.station_edit', (r'^admin/', include(admin.site.urls,
URL adresy jsou zadány regulárním výrazem. ^ říká, že jde o začátek řetězce, $ konec řetězce, (?P<company_id>\d+definuje pojmenovaný podvýraz company_id, jehož hodnota je 1 a více číslic (\d+. Pochopitelně existuje více možností jak definovat regulární výrazy pro URL adresy. Význam mapování: (r'^hello', 'stations.views.hello': po zadání URL stations/hello se zavolá funkce hello ve stations/views s jedním parametrem request. (r'^stations/company/(?p<company_id>\d+', 'stations.views.company_name': po zadání URL stations/company/1 se zavolá funkce company_name ve stations/views, s parametry request a parametrem company_id=1. atd. URL decoupling: V této podobě už můžeme s URL konfigurací skončit, a vše bude fungovat. Nebo můžeme zlepšit přenositelnost naší aplikace stations tím, že URL konfiguraci pro stations přesuneme přímo do aplikace stations, a v URL konfiguraci pro stránku drivers tuto konfiguraci už jen vložíme: Ve složce drivers/stations vytvoříme soubor urls.py, s následujícím obsahem: from django.conf.urls.defaults import * (r'^hello', 'stations.views.hello', (r'^company/(?p<company_id>\d+', 'stations.views.company_name', (r'^company_index', 'stations.views.company_index', (r'^station_index', 'stations.views.station_index', (r'^station/search', 'stations.views.capacity_search', (r'^station/(?p<station_id>\d+/edit', 'stations.views.station_edit', Soubor drivers/urls.py pozměníme: from django.conf.urls.defaults import * # Uncomment the next two lines to enable the admin: from django.contrib import admin admin.autodiscover( (r'^stations/', include('drivers.stations.urls', # (r'^admin/doc/', include('django.contrib.admindocs.urls', (r'^admin/', include(admin.site.urls,
Tím zůstávají URL stejné jako předtím, ale aplikace stations se lépe přenáší, kdybychom ji chtěli zapojit do jiného projektu stačí do urlpatterns projektu přidat: (r'^stations/', include('drivers.stations.urls' Views: Jak bylo řečeno dříve, views jsou funkce definované ve views.py souboru aplikace (drivers/stations/views.py funkce mají jeden povinný parametr request, a podle potřeby další parametry (např. klíč pro pohled na detail objektu, který udává, jaký objekt má být zobrazen View funkce vrací HttpResponse objekt, obsahující data stránky, nebo případně vyhodí výjimku, např. django.http.http404. 1. ve stations/views.py vytvoříme funkci hello (obsluhující dříve nastavenou URL adresu stations/hello: def hello(request: return HttpResponse("Hello, World!" Pokud přejdeme na adresu stations/hello, objeví se stránka s nápisem Hello World 2. ve stations/views.py vytvoříme funkci company_name: def company_name(request, company_id: company = get_object_or_404(company, pk=company_id #company = Company.objects.get(pk=company_id return HttpResponse("Company id:" + company_id + " " + company.name Funkce get_object_or_404 šetří práci, zastupuje následující kód: try: company = Company.objects.get(pk=company_id except Company.DoesNotExist: raise Http404 tzn. pokusí se načíst objekt Company z databáze podle primárního klíče company_id, zadaného jako součást url (viz vysvětlení mapování. Když společnost není nalezena, vyhodí výjimku reprezentující HTTP chybu 404 not found. Templates: Views předchozím způsobem napsané vrací přímo formátovaný text, který bude v těle odpovědi na HTTP požadavek. Tzn. starají se jak o výběr dat z databáze, tak o jejich prezentaci. Pokud bychom chtěli zajímavěji formátovat data vrácená předchozí funkcí, mohlo by to vypadat například takto: def company_name(request, company_id: company = get_object_or_404(company, pk=company_id #company = Company.objects.get(pk=company_id return HttpResponse("<strong>Company id:</strong>" + company_id + " <br/>" + company.name Django umožňuje prezentaci oddělit využitím šablon.
3. company_index: def company_index(request: companies = Company.objects.all(.order_by('-name' return render_to_response('stations/company_index.html', {'companies': companies} Funkce se pokusí načíst šablonu company_index.html a předá jí proměnnou companies, obsahující seznam objektů typu Company získaný předchozím databázovým dotazem Company.objects.all(.order_by('-name'. Šablona company_index.html bude vypadat takto: <h1>company index</h1> {% for company in companies %} <li>{{company.name}}</li> {% for s in company.station_set.all %} <li>{{s.name}}</li> companies obsahuje seznam objektů typu Company, tzn. {% for company in companies %} tímto seznamem iteruje. Pro každý objekt vypíše atribut name v tagu li, stanice patřící společnosti pak vypíše v podseznamu ({% for s in company.station_set.all %}... Pro definici šablony lze použít dědičnost můžeme definovat jednotný styl stránek tím, že vytvoříme základní šablonu (pojmenovanou např. base.html, a ostatní šablony z ní pak dědí: Šablona base.html: <h1>{% block title %}BASE{% endblock %}</h1> <body> {% block body %}{% endblock %} </body> Šablona base definuje dva bloky: title a body. V dědících šablonách pak místo formátování pomocí html tagů budeme používat {% block title %}... {% endblock %} a {% block body %}... {% endblock %} Šablona company_index.html s děděním z šablony base.html: {% extends "stations/base.html" %} {% block title %}Company index{% endblock %} {% block body %} {% for company in companies %} <li>{{company.name}}</li> {% for s in company.station_set.all %}
<li>{{s.name}}</li> {% endblock %} Prozatím aplikace šablony nenajde. Aby věděla, kde hledat, je potřeba upravit proměnnou TEMPLATE_DIRS v settings.py: TEMPLATE_DIRS = (... "templates" Šablony umístíme do templates/stations. Každá view funkce obvykle používá svou šablonu. Zbývá dodělat následující views a jejich příslušné šablony: 1.a: station_index view: def station_index(request: stations = Station.objects.all(.order_by('-name' return render_to_response('stations/station_index.html', {'stations': stations} 1.b: šablona station_index.html: <h1>station index</h1> {% for station in stations %} <li>{{station.name}} -- {{station.size_description}} -- {{station.company.name}}</li> 2.: capacity_search, station_edit: placeholders, doplníme v části o formulářích. def capacity_search(request: return HttpResponse("TBD" def station_edit(request, station_id: return HttpResponse("TBD"