Django: Prezentace dat

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.

Seriál: Hrajeme si s Djangem (16 dílů)

  1. Django: Úvod a instalace 14.8.2009
  2. Django: Nastavení projektu a první pokusy 21.8.2009
  3. Django: Databázový model 28.8.2009
  4. Django: Databázový model podruhé 4.9.2009
  5. Django: Administrace 11.9.2009
  6. Django: Prezentace dat 18.9.2009
  7. Django: Prezentace dat podruhé 25.9.2009
  8. Django: Zpracovávání formulářů 2.10.2009
  9. Django: Autentizace a autorizace 9.10.2009
  10. Django: Nahrávání souborů 16.10.2009
  11. Django: Zabudované aplikace 23.10.2009
  12. Django: Rozšiřování možností Djanga 30.10.2009
  13. Django: Internacionalizace 6.11.2009
  14. Django: Nasazování projektu 13.11.2009
  15. Django: Kešování a škálování 20.11.2009
  16. Django: Závěr 27.11.2009

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é.

Co se týče pojmenování, je vhodné (hlavně ve větších projektech) dodržovat zavedené konvence.

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:

Django 6

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.

Proměnná 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:

Django 6

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

V příštím díle navážeme na toto téma a zkusíme si vytvořit složitější pohledy.

Autor je dlouhodobým studentem Fakulty informatiky, webový nadšenec a programátor — nejraději programuje v jazycích Haskell a Python.

Věděli jste, že nám můžete zasílat zprávičky? (Jen pro přihlášené.)

Komentáře: 16

Přehled komentářů

PMD pro koho?
msgre Re: pro koho?
PMD Re: pro koho?
msgre Re: pro koho?
Pavel Dvořák Re: pro koho?
keff Re: pro koho?
Fedor Re: pro koho?
Kita Re: pro koho?
Botanicus Rango: alternativa Djanga pro Ruby
Martin Malý Re: Rango: alternativa Djanga pro Ruby
Botanicus Re: Rango: alternativa Djanga pro Ruby
hynek Kvůli vytvoření admin.py restartovat server
hynek Omluva Re: Kvůli vytvoření admin.py...
polerok Grafika
Pavel Dvořák Re: Grafika
Django Re: Django: Prezentace dat
Zdroj: https://www.zdrojak.cz/?p=3085