Django: Prezentace dat
Seriál Hrajeme si s Djangem
- Django: Databázový model podruhé
- Django: Administrace
- Django: Prezentace dat
- Django: Prezentace dat podruhé
- Django: Zpracovávání formulářů
Projekt jsme na konci minulého dílu zanechali ve stavu, ve kterém správci mohou pohodlně upravovat data z našich databázových modelů. Nyní se naučíme, jak tato data prezentovat návštěvníkům webu.
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.
Seriál Hrajeme si s Djangem
- Django: Databázový model podruhé
- Django: Administrace
- Django: Prezentace dat
- Django: Prezentace dat podruhé
- Django: Zpracovávání formulářů

