Pod pojmem prezentace dat si můžeme představit spojení pohledu a šablony, při kterém zpracováváme data a zobrazujeme je ve vhodné formě uživatelům. Nejprve si ukážeme několik důležitých vlastností šablonovacího systému.
Strukturování šablon
Když jsem kdysi dávno začal s vytvářením HTML stránek a neuměl pořádně programovat v žádném jazyce, nejhorší ze všeho pro mě bylo zamezit opakování základního kódu, např. obsahu značky <head>
, navigace nebo patičky. Jakákoliv změna na stránkách znamenala otevřít všechny „postižené“ soubory a opravit opakující se kusy HTML kódu. Tehdy jsem na to použil rámy, což se časem ukázalo jako naprostý krok vedle. Teď můžu v Djangu stejný problém jednoduše vyřešit použitím značek šablonovacího systému {% block %}
a {% extends %}
.
Princip si ukážeme na příkladu — nejprve si vytvoříme základní šablonu pro web videopůjčoven. Tento soubor bývá zvykem pojmenovávat base.html
a je potřeba ho uložit do adresáře templates
:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"> <html> <head> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <title>{% block title %}Videopůjčovny s. r. o.{% endblock %}</title> <link rel="stylesheet" href="{{ MEDIA_URL }}css/style.css" type="text/css"> </head> <body> <div id="header">Videopůjčovny s. r. o.</div> <div id="menu"> <ul> <li><a href="/">Titulní strana</a></li> <li><a href="/provozovny/">Provozovny</a></li> <li><a href="/nabidka/">Nabídka titulů</a></li> <li><a href="/kontakt/">Kontakt</a></li> <li><a href="/pro_cleny/">Pro členy</a></li> </ul> </div> <div id="content">{% block content %}{% endblock %}</div> </body> </html>
Pomocí značky {% block %}
si označíme místa, kam se budou vkládat bloky kódu pro jednotlivé stránky. V našem případě to je titulek stránky (title
) a její obsah (content
). Text uvedený uvnitř značek v titulku je výchozí text bloku. Na začátku šablony je též uvedena proměnná {{ MEDIA_URL }}
, která obsahuje cestu ke statických souborům. Poskytuje nám ji takzvaný kontextový procesor django.core.context_processors.media
. Kontextové procesory se využívají v situacích, když potřebujeme dostat do více šablon společné proměnné.
Dále si vytvoříme šablonu pro titulní stranu. Ta využije (rozšíří) základní šablonu a přepíše bloky svým vlastním obsahem. Pojmenujeme ji třeba front_page.html
a uložíme ji také do adresáře templates
:
{% extends "base.html" %} {% block content %} <h1>Dobrý den!</h1> <p>Vítáme vás na webu naší sítě poboček videopůjčoven.</p> {% endblock %}
Na prvním řádku jsme pomocí značky {% extends %}
uvedli, že chceme rozšířit šablonu base.html
. V bloku title
jsme nechali výchozí text, obsah bloku content
byl nahrazen za uvítací blábol. Podobně to budeme dělat i u ostatních šablon.
Generické pohledy
Nejlepší kód je ten, který nemusíme psát. To je důvod, proč nám tvůrci Djanga dali k dispozici několik pohledů, jež dělají naprosto obecné věci — kupříkladu přidání, úpravu nebo zobrazení objektu, a proto se jim říká generické pohledy. Můžeme je nalézt v modulu django.views.generic
a zkusíme si použít nejjednodušší z nich, simple.direct_to_template
, který pouze zobrazí šablonu.
Tento pohled stačí přidat mezi URL (do souboru urls.py
v adresáři projektu) a jako parametr poskytnout slovník obsahující klíč template
:
urlpatterns += patterns('', (r'^admin/', include(admin.site.urls)), # Django 1.0: (r'^admin/(.*)', admin.site.root), (r'^$', 'django.views.generic.simple.direct_to_template', {'template': 'front_page.html'}), )
Když nastartujeme vývojový server (python manage.py runserver
) a přejdeme na adresu http://127.0.0.1:8000/, můžeme vidět titulní stranu webu videopůjčoven:
Místo generického pohledu jsme si v souboru video_store/views.py
mohli vytvořit takovou funkci:
from django.shortcuts import render_to_response from django.template import RequestContext def front_page(request): return render_to_response('front_page.html', {}, context_instance=RequestContext(request))
Jediné co je tu nové, je zavolání funkce RequestContext(request)
, což nám zpřístupní výchozí kontextové procesory, abychom mohli v šabloně využít proměnnou {{ MEDIA_URL }}
. Tento pohled bychom museli stejně přidat do URL, takže používání generických pohledů je mnohem úspornější a elegantnější. Proto si vyzkoušíme použít ještě další, na výpis provozoven. Opět si nejdříve vytvoříme šablonu (templates/stores.html
):
{% extends "base.html" %} {% block content %} <h1>{% block title %}Provozovny{% endblock %}</h1> {% for object in object_list %} <h2>{{ object.store }}</h2> <ul class="store"> <li>{{ object.address }}</li> <li>{{ object.city }}</li> <li>{{ object.postal_code }}</li> {% if object.email %} <li><a href="mailto:{{ object.email }}">{{ object.email }}</a></li> {% endif %} </ul> {% if object.description %} <p>{{ object.description }}</p> {% endif %} {% endfor %} {% endblock %}
Především je tu použit jednoduchý trik, který nám zobrazí v titulku stránky stejný text jako v nadpisu, aniž bychom ho museli psát dvakrát. Dále zde můžeme vidět dvě nové značky, {% for %}
a {% if %}
. První se podobá for cyklu z Pythonu, do proměnné object
přiřadí aktuální prvek ze seznamu object_list
. Druhá značka je hodně podobná pythonovému větvení if, až na to, že nepodporuje větev elif
.
object_list
bude obsahovat seznam provozoven. Výchozí pojmenování této proměnné je možné změnit, podobně jako další parametry, viz dokumentaci.V tomto kódu tedy vypíšeme postupně všechny provozovny a jejich adresy. Pokud je zadána e-mailová adresa, vypíšeme i tu. V opravdovém projektu bych se ji snažil nějakým způsobem ochránit před spamery, třeba pomocí JavaScriptu. Jestliže je u pobočky vyplněný popis, vypíšeme ho také. Znova budeme muset provázat šablonu přes generický pohled s adresou (soubor urls.py
), tentokrát je to trochu složitější, protože musíme specifikovat i výběr dat:
from hrajeme_si.video_store.models import Store stores = { 'queryset': Store.objects.all(), 'template_name': 'stores.html', } urlpatterns += patterns('', (r'^admin/', include(admin.site.urls)), # Django 1.0: (r'^admin/(.*)', admin.site.root), (r'^$', 'django.views.generic.simple.direct_to_template', {'template': 'front_page.html'}), (r'^provozovny/$', 'django.views.generic.list_detail.object_list', stores), )
Z důvodů zpřehlednění jsem si definoval proměnnou stores
, místo toho, abych slovník vkládal přímo do parametru. Generický pohled list_detail.object_list
požaduje klíč queryset
, což je klasický QuerySet
— vybrali jsme všechny záznamy z tabulky modelu Store
. Změna oproti předchozímu generickému pohledu je v tom, že se klíč s názvem šablony nejmenuje template
, ale template_name
. Výsledek našeho snažení vypadá takto:
Pro úplnost si ještě ukážeme, jak by to vypadalo bez použití generického pohledu:
from django.shortcuts import render_to_response from django.template import RequestContext from hrajeme_si.video_store.models import Store def stores(request): return render_to_response('stores.html', {'object_list': Store.objects.all()}, context_instance=RequestContext(request))
Generické pohledy doporučuji používat všude kde to jen jde, vyhnete se tak jednotvárnému opakování kódu. Jedná se o jednoduché výpisy položek, jejich kategorizace podle data, přesměrování stránek a základní práci s formuláři. K tomu se dostaneme v pozdějších dílech seriálu.
Související odkazy
- Šablonovací systém a generické pohledy na Djangoproject.com
- Čtvrtá a jedenáctá kapitola v The Definitive Guide to Django
- Ukázkový příklad ke stažení.
V příštím díle navážeme na toto téma a zkusíme si vytvořit složitější pohledy.
Přehled komentářů