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 214x (133.18 kB)
Aplikace je včetně zdrojových kódů v jazyce Python