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: urlpatterns = patterns('', # Example: # (r'^drivers_demo1/', include('drivers_demo1.foo.urls')), # Uncomment the admin/doc line below and add 'django.contrib.admindocs' # to INSTALLED_APPS to enable admin documentation: # (r'^admin/doc/', include('django.contrib.admindocs.urls')), # Uncomment the next line to enable the admin: (r'^admin/', include(admin.site.urls)), )
Přidáme mapování na naše pohledy: urlpatterns = patterns('', # Example: # (r'^drivers/', include('drivers.foo.urls')), # Uncomment the admin/doc line below and add 'django.contrib.admindocs' # to INSTALLED_APPS to enable admin documentation: # (r'^admin/doc/', include('django.contrib.admindocs.urls')), (r'^stations/hello', 'stations.views.hello'), (r'^stations/company/(?P
\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'), # Uncomment the next line to enable the admin: (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\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\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 * urlpatterns = patterns('', (r'^hello', 'stations.views.hello'), (r'^company/(?P\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() urlpatterns = patterns('', (r'^stations/', include('drivers.stations.urls')), # Uncomment the admin/doc line below and add 'django.contrib.admindocs' # to INSTALLED_APPS to enable admin documentation: # (r'^admin/doc/', include('django.contrib.admindocs.urls')), # Uncomment the next line to enable the admin: (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:" + company_id + "
" + 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: Company index
{% for company in companies %} - {{company.name}}
{% for s in company.station_set.all %} - {{s.name}}
{% endfor %}
{% endfor %}
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: {% block title %}BASE{% endblock %}
{% block body %}{% endblock %}
Š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 %} - {{company.name}}
{% for s in company.station_set.all %}
- {{s.name}}
{% endfor %}
{% endfor %}
{% 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: Station index
{% for station in stations %} - {{station.name}} -- {{station.size_description}} -- {{station.company.name}}
{% endfor %}
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")