Lekce 7 - Databáze filmů v Django - Generic Views a Formuláře
V minulé lekci, Databáze filmů v Django - Databáze, jsme si vytvořili modely, naučili se migrovat databázi a pracovat s Django administrací.
V dnešním tutoriálu webových aplikací s frameworkem Django v Pythonu se budeme věnovat generic views, pomocí kterých naučíme projekt Filmová databáze pracovat s databází.
Úprava souborů Filmové databáze
Než se vrhneme do práce, vytvořme si pomocí databázové administrace v
databázi několik dalších filmů a klidně i žánrů, abychom měli pohledy
na čem zkoušet. Náš detail_filmu() si odstraníme z
views.py, odkaz na tento view odstraníme také z
urls.py a nakonec odstraníme celý soubor
detail_filmu.html ze složky
mysite/moviebook/templates/moviebook/.
Generic views v Djangu
Generic views jsou předpřipravené pohledy pro jednoduché akce, které se ve webových aplikacích často používají. Právě ty využijeme pro přidávání a editaci záznamů v naší databázi, abychom nemuseli psát vše znovu.
ListView
Jako první generic view si vyzkoušíme ListView, který nám
vypíše seznam položek. V našem případě si jím samozřejmě necháme
vypsat všechny filmy v databázi. Soubor views.py ve složce
mysite/moviebook/ nyní upravíme do následující podoby:
from django.shortcuts import render from django.views import generic from .models import Film class FilmIndex(generic.ListView): template_name = "moviebook/film_index.html" # cesta k šabloně ze složky templates (je možné sdílet mezi aplikacemi) context_object_name = "filmy" # pod tímto jménem budeme volat seznam objektů v šabloně # tato metoda nám získává seznam filmů seřazených od největšího id (9,8,7...) def get_queryset(self): return Film.objects.all().order_by("-id")
Je nám již zřejmé, co je obsahem pojmu view.
Nebudeme tedy dál používat mix češtiny (pohled) a angličtiny a budeme se
držet konvenčního anglického označení view.
Popišme si kód. Nejprve je třeba naimportovat generic views a
samotné modely. View nyní již není tvořené pouhou metodou, ale třídou
dědící z generic.ListView, případně z jiného generického
pohledu. Když se nad tím zamyslíme, je to logické, protože právě
dědičností se nám do view dostane předpřipravená funkčnost.
Generic view nastavíme šablonu a jak se má proměnná s
jednotlivými prvky seznamu v šabloně jmenovat. Následně definujeme metodu
pro získání všech filmů, které si seřadíme od posledně přidaných po
ty nejstarší.
Na tento view si vytvoříme odkaz v souboru urls.py ve složce
mysite/moviebook/:
from django.urls import path from . import views urlpatterns = [ path("film_index/", views.FilmIndex.as_view(), name="filmovy_index"), ]
Šablona pro ListView
Šablonu pro náš první generic view vytvoříme ve složce
mysite/moviebook/templates/moviebook/ jako nový soubor
film_index.html s následujícím obsahem:
<!DOCTYPE html> <html lang="cs"> <head> <meta charset="UTF-8"> </head> <body> {% for film in filmy %} Název: {{ film.nazev }} <br> {% endfor %} </body> </html>
Tento soubor ani všechny následující nezapomeňme jako vždy uložit v kódování UTF-8.
Na adrese http://localhost:8000/moviebook/film_index/ se nám po
spuštění serveru zobrazí seznam všech našich stávajících filmů:
DetailView
Nyní by bylo dobré vytvořit si i view pro detail vybraného filmu. K tomu
nám pomůže DetailView, který nám o filmu zobrazí veškeré
podrobnosti. Na konec souboru views.py ve složce
mysite/moviebook/ přidáme:
class CurrentFilm(generic.DetailView): model = Film template_name = "moviebook/film_detail.html"
Třída CurrentFilm je opět odděděná z generického předka.
V případě detailu nastavujeme jen model a název šablony.
Šablona pro DetailView
Šablonu pro DetailView vytvoříme ve složce
mysite/moviebook/templates/moviebook/ jako nový soubor
film_detail.html, který jsme výše nastavili.
Všimněme si pojmenování souborů, kdy je název složený z názvu entity, podtržítka a názvu generic view. Této praxe se budeme držet.
Obsah šablony je následující:
<!DOCTYPE html> <html lang="cs"> <head> <meta charset="UTF-8"> </head> <body> <h1> {{ film.nazev }} </h1> <small> {{ film.rezie }} </small> <h3> {{ film.zanr.nazev_zanru }} </h3> </body> </html>
Každý DetailView potřebuje znát ID/PK
(primární klíč) konkrétního filmu, pro který nám bude zobrazovat
veškeré informace, aby si jej mohl z databáze načíst. Odkaz na film proto
bude obsahovat i PK konkrétního filmu. Upravíme proto ve složce
mysite/moviebook/ soubor urls.py:
from django.urls import path from . import views urlpatterns = [ path("film_index/", views.FilmIndex.as_view(), name="filmovy_index"), path("<int:pk>/film_detail/", views.CurrentFilm.as_view(), name="filmovy_detail"), ]
Tím jsme Django vysvětlili, že když uživatel zadá URL adresu na detail
filmu, číslo před ní je primární klíč (ID) tohoto
filmu.
Nyní odkaz na film přidáme do výpisu filmů, tedy do šablony
film_index.html. Obyčejný uživatel se totiž zatím stále nemá
jak dostat na stránku s informacemi o filmu. Do odkazu nezadáme absolutní
adresu, ale použijeme název URL, který jsme v routách uvedli (v tomto
případě name="filmovy_detail"). Jako parametr předáme
ID/PK. URL je takto jednodušší a kdyby se adresa view někdy
změnila, tato změna se projeví bez nutnosti šablonu upravit. Přidejme tedy
odkaz do film_index.html:
<!DOCTYPE html> <html lang="cs"> <head> <meta charset="UTF-8"> </head> <body> {% for film in filmy %} <a href="{% url 'filmovy_detail' film.id %}"> Název: {{ film.nazev }} <br> </a> {% endfor %} </body> </html>
Po znovunačtení adresy
http://localhost:8000/moviebook/film_index/ budou již filmy v
seznamu jako odkazy:
Po kliknutí na film budeme přesměrováni na detail daného filmu:
Pokud však uživatel bude natolik troufalý a zadá do URL ID
neexistujícího objektu, view nám vyhodí Error 404.
Formulář
Filmy tedy umíme vypisovat a zobrazovat jejich detail. Ale co film přidat
nebo upravit? Za tímto účelem si vytvoříme formulář, což uděláme
pomocí třídy ModelForm. Formulář bychom samozřejmě mohli
vytvořit oldschool cestou jen jako čisté HTML, jako jsme to dělali v
kalkulačce, ale Django tu máme právě proto, abychom se naučili, jak si s
ním ulehčit práci. Vytvoříme si proto ve složce
mysite/moviebook/ nový modul forms.py, ve kterém se
bude nacházet náš formulář:
from django import forms from .models import Film class FilmForm(forms.ModelForm): class Meta: model = Film fields = ["nazev", "rezie", "zanr"]
Jako další krok si (jako už obvykle) vytvoříme view, které bude
stránku s formulářem obsluhovat. Přejdeme do souboru views.py,
kam přidáme nový import právě na náš formulář:
from django.shortcuts import render from django.views import generic from .models import Film from .forms import FilmForm # Nový import # ...
CreateView
Kromě importu na konec souboru přidáme také obsluhu formuláře, kterou
si záhy vysvětlíme. Použijeme pro ní generic view CreateView.
Vidíme, jak můžeme z Django převzít spoustu funkcionality, kterou bychom
jinak museli implementovat sami:
# ... class CreateFilm(generic.edit.CreateView): form_class = FilmForm template_name = "moviebook/create_film.html" # Metoda pro GET request, zobrazí pouze formulář def get(self, request): form = self.form_class(None) return render(request, self.template_name, {"form": form}) # Metoda pro POST request, zkontroluje formulář; pokud je validní, vytvoří nový film; pokud ne, zobrazí formulář s chybovou hláškou def post(self, request): form = self.form_class(request.POST) if form.is_valid(): form.save(commit=True) return render(request, self.template_name, {"form": form})
View nastavujeme formulář a šablonu. Dále obsahuje dvě akce, metoda
get() formulář pouze zobrazuje a metoda post() jej
zpracovává v případě, že již byl odeslán. Všimněme si, že v obou
akcích formulář předáváme pomocí slovníku do šablony, abychom jej tam
mohli vykreslit. Určitě bychom se neměli v metodě post()
zapomenout zeptat, zda byl formulář validně vyplněný. Jelikož formulář
ví, jaký model zpracovává, pro uložení filmu na něm stačí zavolat jen
metodu save() a je hotovo.
Pro nové view a tedy novou adresu si jako vždy přidáme routu v
urls.py:
from django.urls import path from . import views urlpatterns = [ path("film_index/", views.FilmIndex.as_view(), name="filmovy_index"), path("<int:pk>/film_detail/", views.CurrentFilm.as_view(), name="filmovy_detail"), path("create_film/", views.CreateFilm.as_view(), name="novy_film"), ]
Šablona pro CreateView
Vytvoříme si stejně jako v předchozích případech šablonu v novém
souboru create_film.html. Náš formulář zatím bude velice
primitivní:
<!DOCTYPE html> <html lang="cs"> <head> <meta charset="UTF-8"> </head> <body> <form method="POST"> {% csrf_token %} <!-- Django požaduje ověření proti útoku csrf --> {{ form }} <input type="submit"> </form> </body> </html>
Výsledný formulář vypadá takto:
Náš formulář zatím není příliš hezký, ale to v příští lekci
změníme za pomoci django-crispy-forms. Po odeslání validního
formuláře se vytvoří nový film. Po přechodu na
http://localhost:8000/moviebook/film_index si můžeme vyzkoušet,
že se mezi filmy opravdu zobrazí. Naše aplikace začíná být reálně
použitelná 
Zdrojový kód je opět v archivu pod lekcí.
V příští lekci, Databáze filmů v Django - Crispy forms a Bootstrap, si do své aplikace na evidenci filmů integrujeme django-crispy-forms a naučíme se přesměrovávat z výchozí adresy na konkrétní pohled.
Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.
Stáhnout
Stažením následujícího souboru souhlasíš s licenčními podmínkami
Staženo 285x (133.18 kB)
Aplikace je včetně zdrojových kódů v jazyce Python
