W poprzednim rozdziale zapewne zauważyłeś ten szczególny sposób zwracania tekstu w przykładowych widokach. W szczególności kod HTML był na sztywno zakodowany w Pythonie i wyglądał następująco: def current_datetime(request):
now = datetime.datetime.now()
html = "<html><body>Teraz jest %s.</body></html>" % now
return HttpResponse(html)
Choć to rozwiązanie wystarczało, by móc przedstawić zasadę działania widoków, umieszczanie kodu HTML w widokach nie jest dobrym pomysłem. Oto dlaczego:
Z tych powodów znacznie lepiej jest oddzielić wygląd strony od kodu widoku napisanego w Pythonie. Możemy to zrobić, korzystając z systemu szablonów Django opisywanego w niniejszym rozdziale. Podstawy systemu szablonówSzablon Django to łańcuch tekstowy mający na celu odseparowanie prezentacji dokumentu od jego danych. Szablon definiuje miejsca wstawień danych oraz zawiera prostą logikę (znaczniki szablonu), które określając sposób wyświetlania dokumentu. Najczęściej szablony używa się do generowania kodu HTML, ale szablony Django doskonale nadają się do generowania danych w dowolnym formacie tekstowym. Zacznijmy od prostego, przykładowego szablonu. Ten szablon opisuje stronę HTML, która dziękuje osobie za złożenie zamówienia. Potraktuj go jak list formalny: <html>
<head><title>Informacja o zamówieniu</title></head>
<body>
<h1>Informacja o zamówieniu</h1>
<p>Szanowny {{ person_name }},</p>
<p>Dziękujemy za złożenie zamówienia w {{ company }}. Jest ono przewidziane do wysyłki w dniu {{ ship_date|date:"j F Y" }}.</p>
<p>Oto lista zamówionych produktów:</p>
<ul>
{% for item in item_list %}
<li>{{ item }}</li>
{% endfor %}
</ul>
{% if ordered_warranty %}
<p>W paczce znajdzie się informacja na temat gwarancji.</p>
{% else %}
<p>Nie zamawiałeś gwarancji, więc musisz sobie radzić sam w przypadku zepsucia się produktu.</p>
{% endif %}
<p>Z wyrazami szacunku,<br />{{ company }}</p>
</body>
</html>
Ten szablon to podstawowy kod HTML z kilkoma zmiennymi i znacznikami szablonu. Przeanalizujmy go dokładniej:
Każdy szablon Django ma dostęp do kilku wbudowanych znaczników i filtrów, z których wiele jest opisywanych w poniższej sekcji. Dodatek F zawiera pełną listę znaczników i filtrów. Dobrze się z nią zapoznać, by dowiedzieć się, o można osiągnąć. Można także tworzyć własne filtry i znaczniki; tym tematem zajmiemy się w rozdziale 10. Korzystanie z systemu szablonówZagłębmy się w system szablonów, by zobaczyć, jak on działa — na tym etapie nie będziemy jeszcze integrować szablonów z widokami opisanymi w poprzednim rozdziale. Chcemy pokazać działanie szablonów w oderwaniu od pozostałej części Django. Najczęściej system szablonów stosuje się w połączeniu z widokami Django, ale warto pamiętać, że system szablonów to zwykła biblioteka Pythona, z której można korzystać wszędzie, nie tylko w widokach. Oto najprostszy sposób wykorzystania systemu szablonów Django w kodzie Pythona:
Oto w jaki sposób rozwiązanie to wygląda w kodzie: >>> from django import template
>>> t = template.Template("Nazywam się {{ name }}.")
>>> c = template.Context({'name': 'Adrian'})
>>> print t.render(c)
Nazywam się Adrian.
>>> c = template.Context({'name': 'Fred'})
>>> print t.render(c)
Nazywam się Fred.
Poniższe punkty w dokładniejszy sposób opisują działanie zaprezentowanego kodu. Tworzenie obiektów szablonuNajprostszym sposobem utworzenia obiektu typu Template jest bezpośrednie użycie jego klasy. Klasa Template znajduje się w module django.template. Konstruktor przyjmuje jeden argument, którym jest nieprzetworzony kod szablonu. Przejdźmy do interaktywnego interpretera Pythona, by przekonać się jak to działa w praktyce. Z poziomu folderu mysite utworzonego wcześniej poleceniem django-admin.py startproject (patrz rozdział 2.), wpisz python manage.py shell, by uruchomić interaktywny interpreter kodu. Specjalna powłoka Pythona Jeśli korzystałeś wcześniej z Pythona, zapewne zastanawiasz się, dlaczego korzystamy z polecenia python manage.py shell zamiast zwykłego python. Oba polecenia uruchamiają interaktywny interpreter, ale polecenie shell wykonuje coś jeszcze: przed uruchomieniem interpretera informuje Django, z którego pliku ustawień należy skorzystać. Wiele części Django, włączając w to system szablonów, korzysta z pliku ustawień. Nie będziesz mógł ich używać, jeśli nie będą znały lokalizacji pliku ustawień. Jeśli jesteś ciekaw, oto jak wszystko działa "od środka". Django szuka zmiennej środowiskowej nazwanej DJANGO_SETTINGS_MODULE, która powinna wskazywać ścieżkę importowania pliku ustawień settings.py. Przykładowo DJANGO_SETTINGS_MODULE może być równe 'mysite.settings', zakładając, że mysite znajduje się w ścieżce wyszukiwania Pythona. Gdy uruchamiasz polecenie python manage.py shell, zajmuje się ono automatycznym ustawieniem zmiennej środowiskowej DJANGO_SETTINGS_MODULE. Zalecamy stosowanie tego polecenia w celu zminimalizowania liczby kroków konfiguracyjnych. Gdy będziesz bardzo często korzystał z Django, zapewne przestaniesz używać polecenia manage.py shell na rzecz ręcznego ustawiania DJANGO_SETTINGS_MODULE w pliku .bash_profile lub innym pliku konfiguracyjnym powłoki. Prześledźmy kilka podstawowych elementów szablonów: >>> from django.template import Template
>>> t = Template("Nazywam się {{ name }}.")
>>> print t
Jeśli wykonujesz polecenia w trybie interaktywnym, na ekranie zobaczysz poniższy wynik: <django.template.Template object at 0xb7d5f24c> Adres 0xb7d5f24c będzie za każdym razem inny. Nie przejmuj się nim; nie ma dla nas żadnego znaczenia. To adres w pamięci, pod którym interpreter Pythona przechowuje obiekt. Tworząc obiekt Template system szablonów kompiluje nieprzetworzony kod szablony do wewnętrznej, zoptymalizowanej postaci gotowej do zrenderowania. Jeśli jednak kod szablonu zawiera dowolne błędy składniowe, wywołanie Template spowoduje zgłoszenie wyjątku TemplateSyntaxError: >>> from django.template import Template
>>> t = Template('{% notatag %}')
Traceback (most recent call last):
File "<stdin>", line 1, in ?
...
django.template.TemplateSyntaxError: Invalid block tag: 'notatag'
Termin "znacznik blokowy" (ang. block tag) dotyczy {% notatag %}. "Znacznik blokowy" i "znacznik szablonu" to synonimy. System zgłasza wyjątek TemplateSyntaxError dla każdego z podanych niżej przypadków:
Rendering szablonuPo utworzeniu obiektu Template możemy zacząć przekazywać do niego dane, wykorzystując tzw. kontekst. Kontekst to po prostu zestaw nazw zmiennych i powiązanych z nimi wartości. Szablon używa tych danych do zastąpienia zmiennych i wyliczenia znaczników. Kontekst w Django reprezentuje klasa Context znajdująca się w module django.template. Konstruktur klasy przyjmuje jeden opcjonalny argument: słownik odwzorowujący nazwy zmiennych na wartości. Wywołaj metodę render() szablonu przekazując kontekst, aby wypełnić szablon danymi: >>> from django.template import Context, Template
>>> t = Template("Nazywam się {{ name }}.")
>>> c = Context({"name": "Stephane"})
>>> t.render(c)
u'Nazywam się Stephane.'
W tym miejscu warto wspomnieć, że wartością zwracaną przez t.render(c) jest obiekt Unicode, a nie standardowy tekst. Łatwo to rozpoznać po u przez apostrofem. Django używa obiektów unikodowych zamiast standardowych tekstów w całym frameworku. Jeśli rozumiesz reperkusje takiego podejścia, podziękuj Django, że nie musisz samemu zajmować się całą złożonością procesu. Jeśli nie rozumiesz reperkusji, nie przejmuj się nimi; wystarczy, że będziesz wiedział, iż obsługa Unicode w Django umożliwia łatwe i bezbolesne korzystanie z różnych zestawów znaków (między innymi polskich znaków diakrytycznych) w całym systemie. Słowniki i konteksty Słownik Pythona to odwzorowanie znanych kluczy na odpowiadające im wartości. Obiekt Context jest bardzo podobny do słownika, ale zapewnia dodatkową funkcjonalność opisywaną dokładniej w rozdziale 10. Nazwy zmiennych muszą zaczynać się od litery (A-Z lub a-z) i mogą zawierać cyfry, znaki podkreślenia i kropki (kropki mają specjalne znaczenie, a czym za chwilę). Nazwy zmiennych są czułe na wielkość liter. Poniżej znajduje się kod kompilujący i renderujący szablon bardzo podobny do tego, który został zaprezentowany na początku rozdziału: >>> from django.template import Template, Context
>>> raw_template = """<p>Szanowny {{ person_name }},</p>
...
... <p>Dziękujemy za złożenie zamówienia w {{ company }}. Jest ono przewidziane do wysyłki w dniu {{ ship_date|date:"j F Y" }}.</p>
...
... {% if ordered_warranty %}
... <p>W paczce znajdzie się informacja na temat gwarancji.</p>
... {% else %}
... <p>Nie zamawiałeś gwarancji, więc musisz sobie radzić sam w przypadku zepsucia się produktu.</p>
... {% endif %}
...
... <p>Z wyrazami szacunku,<br />{{ company }}</p>"""
>>> t = Template(raw_template)
>>> import datetime
>>> c = Context({'person_name': 'Jan Kowalski',
... 'company': 'Outdoor Equipment',
... 'ship_date': datetime.date(2009, 4, 2),
... 'ordered_warranty': False})
>>> t.render(c)
u"<p>Szanowny Jan Kowalski,</p>\n\n<p>Dziękujemy za złożenie zamówienia w Outdoor Equipment. Jest ono przewidziane do wysyłki w dniu 2 April 2009.</p>\n\n\n<p>Nie zamawiałeś gwarancji, więc musisz sobie radzić sam w przypadku zepsucia się produktu.</p>\n\n\n<p>Z wyrazami szacunku,<br />Outdoor Equipment</p>"
Przeanalizujmy kod krok po kroku:
To podstawy stosowania systemu szablonów Django: napisz treść szablonu, utwórz obiekt Template, utwórz obiekt Context i wywołaj metodę render(). Ten szam szablon, wiele kontekstówPo utworzeniu obiektu Template można go wykorzystać do zrenderowania wielu kontekstów. Oto przykład: >>> from django.template import Template, Context
>>> t = Template('Witaj, {{ name }}')
>>> print t.render(Context({'name': 'Janie'}))
Witaj, Janie
>>> print t.render(Context({'name': 'Julie'}))
Witaj, Julie
>>> print t.render(Context({'name': 'Pat'}))
Witaj, Pat
Jeśli wykorzystujesz to samo źródło szablonu do zrenderowania wielu kontekstów, znacznie wydajniej jest jednokrotnie utworzyć obiekt Template i wielokrotnie wywołać jego metodę render(): # Źle
for name in ('Janie', 'Julie', 'Pat'):
t = Template('Witaj, {{ name }}')
print t.render(Context({'name': name}))
# Dobrze
t = Template('Witaj, {{ name }}')
for name in ('Janie', 'Julie', 'Pat'):
print t.render(Context({'name': name}))
System analizy składniowej szablonów Django jest dosyć szybki. W zasadzie większość analizy przeprowadza się za pomocą pojedynczego wyrażenia regularnego. Rozwiązanie to jest znacznie bardziej optymalne od systemów szablonowych opartych na XML, które to nierzadko wprowadzają narzut związany z analizą XML i są tym samym dziesiątki razy wolniejsze od systemu renderowania Django. Wyszukiwanie zmiennej kontekstowejW prezentowanych do tej pory przykładach jako kontekst przekazywaliśmy proste wartości — głównie tekst, ale i obiekt datetime.date. system szablonu elegancko obsługuje również bardziej złożone struktury danych, na przykład listy, słowniki i dowolne obiekty. Kluczem do efektywnego pobierania danych ze złożonych struktur w szablonach Django jest znak kropki (.). Użyj kropki, by uzyskać dostęp do kluczy słownika, atrybutów, indeksów i metod obiektów. Najlepiej można to zilustrować kilkoma przykładami. Przypuśćmy, że przekazujemy do szablonu słownik. Aby uzyskać dostęp do kluczy słownika, używamy notacji kropkowej: >>> from django.template import Template, Context
>>> person = {'name': 'Sally', 'age': '43'}
>>> t = Template('{{ person.name }} ma {{ person.age }} lata.')
>>> c = Context({'person': person})
>>> t.render(c)
u'Sally ma 43 lata.'
W podobny sposób kropki zapewniają dostęp do atrybutów obiektu. Obiekt Pythona datetime.date zawiera atrybuty year, month i day. W szablonie za pomocą notacji kropkowej łatwo odczytamy wartości atrybutów: >>> from django.template import Template, Context
>>> import datetime
>>> d = datetime.date(1993, 5, 2)
>>> d.year
1993
>>> d.month
5
>>> d.day
2
>>> t = Template('Miesiącem jest {{ date.month }} a rokiem jest {{ date.year }}.')
>>> c = Context({'date': d})
>>> t.render(c)
u'Miesiącem jest 5 a rokiem jest 1993.'
Poniższy przykład wykorzystuje klasę użytkownika, pokazując, że notacja kropkowa zapewnia dostęp do dowolnych właściwości dowolnych obiektów: >>> from django.template import Template, Context
>>> class Person(object):
... def __init__(self, first_name, last_name):
... self.first_name, self.last_name = first_name, last_name
>>> t = Template('Witaj, {{ person.first_name }} {{ person.last_name }}.')
>>> c = Context({'person': Person('Jan', 'Kowalski')})
>>> t.render(c)
u'Witaj, John Smith.'
Notacja kropkowa współpracuje również z metodami obiektów. Każdy ciąg znaków w Pythonie zawiera metody upper() i isdigit(). Można je wywołać, używając tej samej notacji kropkowej: >>> from django.template import Template, Context
>>> t = Template('{{ var }} -- {{ var.upper }} -- {{ var.isdigit }}')
>>> t.render(Context({'var': 'witaj'}))
u'witaj -- WITAJ -- False'
>>> t.render(Context({'var': '123'}))
u'123 -- 123 -- True'
Zauważ, że nie umieszcza się nawiasów w wywołaniach metod. Do metod nie można także przekazać argumentów, więc możliwe jest wywołanie tylko metod nie mających wymaganych parametrów. (Powody tych ograniczeń wyjaśnimy w dalszej części rozdziału). Notacja kropkowa służy również do pobierania zawartości indeksów list: >>> from django.template import Template, Context
>>> t = Template('Element 2 to {{ items.2 }}.')
>>> c = Context({'items': ['gruszki', 'banany', 'marchewki']})
>>> t.render(c)
u'Element 2 to marchewki.'
Nie dopuszcza się stosowania ujemnych wartości indeksów. Przykładowo umieszczenie w szablonie tekstu {{ items.-1 }} spowoduje zgłoszenie wyjątku TemplateSyntaxError. Listy Pythona Przypomnijmy, że listy w języku Python stosując indeksy zaczynające się od 0. Pierwszy element ma indeks 0, drugi 1 itd. Notację kropkową można podsumować w następujący sposób: jeśli system szablonów odnajdzie kropkę w nazwie zmiennej, próbuje wyszukać zawartość wewnątrz zmiennej w następującej kolejności:
System stosuje pierwsze rozwiązanie, które działa, pomijając w takiej sytuacji wszystkie pozostałe przypadki. Notacja kropkowa może być wielokrotnie zagnieżdżona. Poniższy przykład wykorzystuje zmienną {{ person.name.upper }}, która przekłada się najpierw na wyszukanie w słowniku (person['name']), a następnie na wywołanie metody (upper()): >>> from django.template import Template, Context
>>> person = {'name': 'Sally', 'age': '43'}
>>> t = Template('{{ person.name.upper }} ma {{ person.age }} lata.')
>>> c = Context({'person': person})
>>> t.render(c)
u'SALLY ma 43 lata.'
Zachowanie wywoływania metodWywołania metod są nieco bardziej złożone niż inne typy wyszukań. Oto kilka kwestii, o których warto pamiętać:
Obsługa nieprawidłowych zmiennychDomyślnie, jeśli zmienna nie istnieje, szablon renderuje ją jako pusty tekst bez zgłaszania błędu. Oto przykład: >>> from django.template import Template, Context
>>> t = Template('Twoje imię to {{ name }}.')
>>> t.render(Context())
u'Twoje imię to .'
>>> t.render(Context({'var': 'hello'}))
u'Twoje imię to .'
>>> t.render(Context({'NAME': 'hello'}))
u'Twoje imię to .'
>>> t.render(Context({'Name': 'hello'}))
u'Twoje imię to .'
System pomija błąd po chichu zamiast zgłaszać wyjątek, ponieważ ma być nieczuły na ludzkie błędy, które się czasem zdarzają. W tym przypadku wszystkie wyszukania nie powiodły się z powodu ich braku lub złej wielkości liter. W rzeczywistym świecie jest niedopuszczalne, by witryna przestała być dostępna z powodu tak niewielkiej literówki. Pamiętaj, że można zmienić domyślne zachowanie Django w kwestii błędów w szablonie, modyfikując ustawienia konfiguracyjne. Więcej informacji na ten temat pojawi się w rozdziale 10. Zabawa z obiektami kontekstuW większości sytuacji obiekty Context tworzy się, przekazując w pełni wypełniony słownik w wywołaniu konstruktora. Nic jednak nie stoi na przeszkodzie, by usuwać lub dodawać elementy do kontekstu już po jego utworzeniu, wykorzystując w tym celu standardową składnię słownikową Pythona: >>> from django.template import Context
>>> c = Context({"foo": "bar"})
>>> c['foo']
'bar'
>>> del c['foo']
>>> c['foo']
Traceback (most recent call last):
...
KeyError: 'foo'
>>> c['newvariable'] = 'witaj'
>>> c['newvariable']
'witaj'
Podstawowe filtry i znaczniki szablonówJak wspomnieliśmy wcześniej, system szablonów zawiera wbudowany zestaw znaczników i filtrów. Ta część rozdziału zawiera krótki opis najpopularniejszych znaczników i filtrów. Znacznikiif/elseZnacznik {% if %} analizuje zmienną i jeśli jej wartość odpowiada wartości prawdy (czyli istnieje, nie jest pusta i nie jest fałszem), system wyświetli wszystko między {% if %} i {% endif %}. Oto przykład: {% if today_is_weekend %}
<p>Ciesz się weekendem!</p>
{% endif %}
Znacznik {% else %} jest opcjonalny: {% if today_is_weekend %}
<p>Ciesz się weekendem!</p>
{% else %}
<p>Wracaj do pracy.</p>
{% endif %}
"Prawda" w Pythonie W Pythonie oraz w szablonach Django poniższe obiekty odpowiadają fałszowi w kontekście wartości logicznych:
Wszystko inne traktowane jest jako wartość logiczna prawdy. Znacznik {% if %} obsługuje operatory and, or lub not w celu testowania kilku zmiennych lub zanegowania wartości zmiennej. Przykłady: {% if athlete_list and coach_list %}
Dostępni są atleci i trenerzy.
{% endif %}
{% if not athlete_list %}
Nie ma atletów.
{% endif %}
{% if athlete_list or coach_list %}
Są jacyś atleci lub trenerzy.
{% endif %}
{% if not athlete_list or coach_list %}
Nie ma atletów lub są jacyś trenerzy.
{% endif %}
{% if athlete_list and not coach_list %}
Są jacyś atleci i nie ma trenerów.
{% endif %}
Znacznik {% if %} nie dopuszcza jednoczesnego użycia operatorów and i or, ponieważ w takiej sytuacji logika działania byłaby niejednoznaczna. Przykładowo poniższy zapis nie jest poprawny: {% if athlete_list and coach_list or cheerleader_list %}
Użycie nawiasów do sterowania kolejnością operacji nie jest obsługiwane. Jeśli czujesz, że musiałbyś zastosować nawiasy, zastanów się nad obsługą logiki poza szablonami i przekaż wynik jako osobną zmienną szablonową. Ewentualnie użyj zagnieżdżonych znaczników {% if %} jak w przykładzie poniżej: {% if athlete_list %}
{% if coach_list or cheerleader_list %}
Mamy atletów i trenerów lub cheerleaderki!
{% endif %}
{% endif %}
Wielokrotne użycie tego samego operatora nie stanowi problemu. Nie można jedynie użyć różnych kombinacji operatorów. Poniższy kod jest poprawny: {% if athlete_list or coach_list or parent_list or teacher_list %}
Nie ma znacznika {% elif %}. W takiej sytuacji użyj zagnieżdżonych znaczników {% if %}, by osiągnąć ten sam efekt: {% if athlete_list %}
<p>Oto atleci: {{ athlete_list }}.</p>
{% else %}
<p>Brak atletów.</p>
{% if coach_list %}
<p>Oto trenerzy: {{ coach_list }}.</p>
{% endif %}
{% endif %}
Pamiętaj o zamknięciu każdego {% if %} znacznikiem {% endif %}. W przeciwnym razie Django zgłosi wyjątek TemplateSyntaxError. forznacznik {% for %} umożliwia przejście przez każdy element pewnej sekwencji. Podobnie jak w Pythonie, stosowaną składnią jest for X in Y, gdzie ``Y to sekwencja do przeanalizowania, a X to nazwa zmiennej wykorzystywanej w konkretnym cyklu pętli. W każdym cyklu pętli system szablonów będzie renderował wszystko, co znajdzie się między znacznikami {% for %} i {% endfor %}. Przykładowo można użyć poniższego kodu do wyświetlenia listy atletów zawartej w zmiennej athlete\_list: <ul>
{% for athlete in athlete_list %}
<li>{{ athlete.name }}</li>
{% endfor %}
</ul>
Dodanie słowa reversed do znacznika powoduje iterację przez elementy w odwrotnej kolejności: {% for athlete in athlete_list reversed %}
...
{% endfor %}
Można zagnieżdżać znaczniki {% for %}: {% for athlete in athlete_list %}
<h1>{{ athlete.name }}</h1>
<ul>
{% for sport in athlete.sports_played %}
<li>{{ sport }}</li>
{% endfor %}
</ul>
{% endfor %}
Częstym wzorcem jest sprawdzanie rozmiaru listy przed rozpoczęciem pętli oraz wyświetlenie tekstu, jeśli pętla jest pusta: {% if athlete_list %}
{% for athlete in athlete_list %}
<p>{{ athlete.name }}</p>
{% endfor %}
{% else %}
<p>Nie ma atletów. Są tylko programiści komputerowi.</p>
{% endif %}
Ponieważ wzorzec ten jest tak często powtarzamy, znacznik for obsługuje opcjonalną klauzulę {% empty %}, która umożliwia określenie wyniku danego fragmentu w przypadku pustej pętli: {% for athlete in athlete_list %}
<p>{{ athlete.name }}</p>
{% empty %}
<p>Nie ma atletów. Są tylko programiści komputerowi.</p>
{% endfor %}
System nie obsługuje wcześniejszego zakończenia pętli (przed przeanalizowaniem wszystkich elementów). Jeśli chcesz osiągnąć coś takiego, zmień zmienną przekazywaną do szablonu w taki sposób, by zawierała jedynie potrzebne wartości. Podobnie nie istnieje obsługa kontynuacji, która powodowałaby automatyczne przejście do następnego elementu pętli. (Powody tych decyzji zostały dokładnie opisane w dalszej części rozdziału w sekcji "Filozofie i ograniczenia"). Wewnątrz pętli {% for %} masz dostęp do zmiennej szablonowej nazwanej forloop. Zmienna zawiera kilka atrybutów informujących o postępach w wykonaniu pętli:
Magiczna zmienna forloop jest dostępna tylko wewnątrz pętli. Po osiągnięciu znacznika {% endfor %} znika. Kontekst i zmienna forloop Wewnątrz bloku {% for %} istniejąca zmienna jest usuwana, by zrobić miejsce dla zmiennej forloop. Django istniejącą zmienną o tej nazwie udostępnia jako forloop.parentloop. Najczęściej nie trzeba się tym przejmować, ale gdybyśmy sami przekazali do szablonu zmienną o nazwie forloop (choć tego nie zalecamy), wewnątrz bloku {% for %} będzie ona nosiła nazwę forloop.parentloop. ifequal/ifnotequalSystem szablonów Django celowo nie jest pełnym językiem programowania i nie pozwala na wykonywanie dowolnych poleceń języka Python. (Więcej na ten temat w części "Filozofie i ograniczenia"). Często zdarza się, że chcemy porównać dwie wartości i wyświetlić coś, jeśli są one równe — Django na te okoliczność udostępnia znacznik {% ifequal %}. Znacznik {% ifequal %} porównuje dwie wartości i wyświetla wszystko między {% ifequal %} i {% endifequal %}, jeśli wartości są sobie równe. Poniższy przykład porównuje zmienne szablonu user i currentuser: {% ifequal user currentuser %}
<h1>Witaj!</h1>
{% endifequal %}
Argumenty mogą być stałymi tekstami umieszczonymi w apostrofach lub cudzysłowach, więc poprawne są poniższe wartości: {% ifequal section 'sitenews' %}
<h1>Wiadomości</h1>
{% endifequal %}
{% ifequal section "community" %}
<h1>Społeczność</h1>
{% endifequal %}
Podobnie jak znacznik {% if %}, także znacznik {% ifequal %} obsługuje opcjonalną klauzulę {% else %}: {% ifequal section 'sitenews' %}
<h1>Wiadomości</h1>
{% else %}
<h1>Tutaj nie ma wiadomości</h1>
{% endifequal %}
Jako argumenty znacznika {% ifequal %} dopuszcza się tylko zmienne szablonów, teksty, liczby całkowite i zmiennoprzecinkowe. Oto poprawne przykłady użycia: {% ifequal variable 1 %}
{% ifequal variable 1.23 %}
{% ifequal variable 'foo' %}
{% ifequal variable "foo" %}
Inne rodzaje zmiennych, na przykład słowniki, listy lub wartości logiczne nie mogą zostać użyte jako stałe w znaczniku {% ifequal %}. Oto niepoprawne przykłady użycia: {% ifequal variable True %}
{% ifequal variable [1, 2, 3] %}
{% ifequal variable {'key': 'value'} %}
Jeśli chcesz sprawdzić, czy coś jest prawda lub fałszem, użyj znacznika {% if %} zamiast znacznika {% ifequal %}. KomentarzePodobnie jak w HTML lub Pythonie, system szablonów Django dopuszcza stosowanie komentarzy. Aby utworzyć komentarz, użyj konstrukcji {# #}: {# To jest komentarz #}
Komentarz nie stanie się częścią wyniku zrenderowanego szablonu. Komentarze stosujące powyższą składnię nie mogą obejmować wielu wierszy. To ograniczenie zwiększa wydajność analizatora składniowego. Poniższy szablon będzie wyglądał dokładnie tak samo niezależnie od tego, czy znajduje się w postaci oryginalnej, czy zrenderowanej (konstrukcja komentarza nie zostanie rozpoznana): To jest {# to nie jest
komentarz #}
test.
Jeśli chcesz użyć komentarza wielowierszowego, zastosuj znacznik {% comment %} w następujący sposób: {% comment %}
To jest komentarz
wielowierszowy.
{% endcomment %}
FiltryJak wyjaśniono we wcześniejszej części rozdziału, filtry szablonów to bardzo prosty sposób na zmianę zawartości zmiennej przez jej wyświetleniem. Filtry wykorzystują znak pionowej kreski: {{ name|lower }}
Kod wyświetla zawartość {{ name }} po wcześniejszym przetworzeniu przez filtr lower, który konwertuje tekst na małe litery. Można tworzyć łańcuchy filtrów — w ten sposób wynik działania jednego filtru trafia do następnego. Poniżej znajduje się przykład, który pobiera pierwszy element listy i konwertuje go na duże litery: {{ my_list|first|upper }}
Niektóre filtry przyjmują argumenty. Argument filtru znajduje się po znaku dwukropka i zawsze wewnątrz cudzysłowów. Oto przykład: {{ bio|truncatewords:"30" }}
Filtr powoduje wyświetlenie pierwszych 30 słów zmiennej bio. Poniżej prezentujemy kilka najistotniejszych filtrów. Pełna lista znajduje się w dodatku F:
Filozofie i ograniczeniaPo zapoznaniu się ze sposobem działania systemu szablonów Django, warto byłoby wyjaśnić kilka jego ograniczeń wraz z kilkoma filozofiami dotyczącymi zaimplementowania go w stanie, w którym istnieje. Składnia szablonów, bardziej niż inne komponenty aplikacji internetowych, są bardzo subiektywne i różnie odbierane przez programistów. Fakt, że Python posiada dziesiątki jeśli nie setki udostępnionych publicznie implementacji różnych systemów szablonów stanowi tego ewidentny dowód. Każdy powstał zapewne z przeświadczenia, że wszystkie istniejące języki szablonowe są nieodpowiednie. (W zasadzie część programistów Pythona za punkt honoru przyjmuje napisanie własnego języka szablonowego! Jeśli jeszcze tego nie zrobiłeś, rozważ taką ewentualność. To zabawne ćwiczenie). Pamiętaj, że Django nie wymaga stosowania wbudowanego języka szablonów. Ponieważ Django zostało napisane jako całościowy framework do produktywnego tworzenia aplikacji internetowych, często bardziej wygodnym okazuje się użycie systemu wbudowanego niż innych bibliotek systemów szablonów. Nie jest to jednak wymóg. Jak przekonasz się w dalszej części rozdziału, z Django można wykorzystać dowolny inny język szablonów Pythona. Mamy bardzo silne przeświadczenie co do poprawności sposobu działania szablonów Django. System szablonów wywodzi się ze sposobu pracy w World Online i łączy w sobie doświadczenia twórców Django. Oto kilka tych filozofii:
Korzystanie z szablonów w widokachPo zapoznaniu się z podstawami systemu szablonów wykorzystajmy zdobytą wiedzę do utworzenia widoku. Przypomnijmy sobie widok current_datetime z mysite.views przedstawiony w poprzednim rozdziale. Oto jaką miał postać: from django.http import HttpResponse
import datetime
def current_datetime(request):
now = datetime.datetime.now()
html = "<html><body>Teraz jest %s.</body></html>" % now
return HttpResponse(html)
Zmieńmy widok tak, by używał szablonów Django. Zapewne początkowo chciałbyś użyć poniższego kodu:
Oczywiście taki kod używa systemu szablonów, ale nie rozwiązuje problemów wskazanych na początku rozdziału. Szablon nadal znajduje się w kozie Pythona, więc trudno mówić o prawdziwej separacji danych i prezentacji. Rozwiążmy problem, umieszczając szablon w osobnym pliku, który widok wczyta. Zastanówmy się najpierw nad umieszczeniem szablonu w systemie plików i użyciem funkcji wczytywania plików Pythona, by załadować zawartość szablonu. Oto, w jaki sposób mogłoby to wyglądać, jeśli założymy, że zawartość szablonu znajduje się w pliku /home/djangouser/templates/mytemplate.html: from django.template import Template, Context
from django.http import HttpResponse
import datetime
def current_datetime(request):
now = datetime.datetime.now()
# Prosty sposób użycia szablonu z systemu plików.
# To ZŁE rozwiązanie, bo nie bierze pod uwagę brakujących plików!
fp = open('/home/djangouser/templates/mytemplate.html')
t = Template(fp.read())
fp.close()
html = t.render(Context({'current_date': now}))
return HttpResponse(html)
Rozwiązanie to nie jest eleganckie z kilku powodów:
Aby rozwiązać te problemy, skorzystamy z wczytywania szablonów i folderów szablonów. Wczytywanie szablonówDjango stosuje przyjazny i użyteczny interfejs do wczytywania szablonów z systemu plików, który ma za zadanie zmniejszyć redundancję zarówno wywołań wczytywania szablonów, jak i samych szablonów. Aby użyć interfejsu wczytywania szablonów, trzeba poinformować system, gdzie znajdują się szablony. Miejscem na tego rodzaju dane jest plik ustawień — plik settings.py wspomniany w poprzednim rozdziale w trakcie omawiania konfiguracji ROOT_URLCONF. Jeśli chcesz wykonać prezentowany przykład, otwórz plik settings.py i znajdź ustawienie TEMPLATE_DIRS. Domyślnie jest to pusta krotka wraz z automatycznie wygenerowanym komentarzem: TEMPLATE_DIRS = (
# Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
# Always use forward slashes, even on Windows.
# Don't forget to use absolute paths, not relative paths.
)
Ustawienie to informuje system wczytywania szablonów Django, gdzie należy szukać szablonów. Wybierz folder, w którym chciałbyś przechowywać szablony i dodaj go do TEMPLATE_DIRS w następujący sposób: TEMPLATE_DIRS = (
'/home/django/mysite/templates',
)
Warto zwrócić uwagę na kilka kwestii:
Po ustawieniu folderu TEMPLATE_DIRS, następnym krokiem jest zmiana kodu widoku w taki sposób, by korzystał że ścieżek z pliku ustawień zamiast z zakodowanych na stałe wartości. Powróćmy do naszego przykładowego widoku current_datetime, modyfikując go nieznacznie: from django.template.loader import get_template
from django.template import Context
from django.http import HttpResponse
import datetime
def current_datetime(request):
now = datetime.datetime.now()
t = get_template('current_datetime.html')
html = t.render(Context({'current_date': now}))
return HttpResponse(html)
W tym przykładzie stosujemy funkcję django.template.loader.get_template() zamiast ręcznego wczytywania szablonu z systemu plików. Funkcja get_template() przyjmuje jako parametr nazwę szablonu, poszukuje go w systemie plików, otwiera szablon i zwraca skompilowany obiekt Template. Naszym szablonem w tym przykładzie jest current_datetime.html, ale nie ma nic szczególnego w samym rozszerzeniu pliku .html. Szablony mogą mieć dowolne rozszerzenie, a nawet nie mieć go wcale. Aby określić położenie szablonu w systemie plików, funkcja get_template() łączy foldery podane w TEMPLATE_DIRS z nazwą szablonu przekazaną do funkcji. Jeśli TEMPLATE_DIRS zawiera wartość '/home/django/mysite/templates', powyższe wywołanie get_template() szukałoby szablonu w lokalizacji /home/django/mysite/templates/current\_datetime.html. Jeśli funkcja get_template() nie może odnaleźć szablonu o podanej nazwie zgłasza wyjątek TemplateDoesNotExist. Aby przekonać się, jak wygląda efekt zgłoszenia takiego wyjątku, uruchom serwer deweloperski poleceniem python manage.py runserver z poziomu projektu. Następnie w przeglądarce internetowej wskaż stronę aktywująca widok current_datetime (np. http://127.0.0.1:8000/time/). Jeśli DEBUG jest ustawione na wartość True i nie utworzyłeś jeszcze pliku current_datetime.html, zobaczysz stronę błędu Django wskazującą na wyjątek TemplateDoesNotExist.
Strona błędu podobna jest do strony przedstawionej w rozdziale 3., ale zawiera dodatkowy materiał. Materiał ten wskazuje, jakie pliki próbował wczytać szablon i co było powodem niepowodzenia (np. brak pliku). Tego rodzaju pomoc okazuje się nieoceniona, gdy starasz się poznać przyczyny problemów z szablonami. Utwórz plik current_datetime.html w folderze template, stosując poniższy kod jako treść szablonu: <html><body>Teraz jest {{ current_date }}.</body></html>
Odśwież stronę w przeglądarce internetowej. Powinieneś zobaczyć w pełni zrenderowaną stronę. render_to_response()Pokazaliśmy, w jaki sposób wczytać szablon, wypełnić kontekst i zwrócić obiekt HttpResponse zawierający zrenderowany szablon. Zastosowaliśmy funkcję get_template() zamiast na sztywno zakodowanej ścieżki i logiki pobierania pliku. Nadal jednak wysłanie wyniku wymaga wielu kroków. Ponieważ zaprezentowana logika dotyczy niemal każdego widoku, Django zapewnia skrót, który pozwala wczytać szablon, zrenderować go i zwrócić obiekt HTTPResponse — wszystko to w jednym wierszu kodu. Skrótem tym jest funkcja o nazwie render_to_response() znajdującą się w module django.shortcuts. W większości przypadków będziesz chciał korzystać z tej funkcji zamiast ręcznie wczytywać szablony, tworzyć kontekst i generować obiekt HttpResponse — wyjątkiem może być sytuacja, gdy jesteś rozliczany z liczby napisanych wierszy kodu. Poniżej znajduje się funkcja current_datetime przepisana tak, by korzystała z funkcji render_to_response(): from django.shortcuts import render_to_response
import datetime
def current_datetime(request):
now = datetime.datetime.now()
return render_to_response('current_datetime.html', {'current_date': now})
Istotna różnica! Omówmy wprowadzone zmiany:
Pierwszym argumentem funkcji render_to_response() jest nazwa szablonu. Drugim argumentem jest słownik używany do utworzenia obiektu Context dla szablonu. Jeśli drugi argument nie zostanie przekazany, funkcja użyje pustego słownika. Sztuczka z locals()Przyjrzyjmy się raz jeszcze naszej ostatniej wersji widoku current_datetime: def current_datetime(request):
now = datetime.datetime.now()
return render_to_response('current_datetime.html', {'current_date': now})
Wielokrotnie znajdziesz się w sytuacji podobnej do tej w przykładzie: wyliczysz pewne wartości, zapamiętasz je w zmiennej i następnie wyślesz do szablonu. Szczególnie leniwi programiści zauważyliby, że stosowanie nazw zmiennych i zmiennych szablonu to redundancja. Co więcej, to dodatkowe znaki. Jeśli jesteś jednym z tych leniwych programistów i chciałbyś uzyskać bardzo zwięzły kod, możesz skorzystać z wbudowanej w język Python funkcji locals(). Zwraca ona słownik odwzorowujący wszystkie lokalne nazwy zmiennych na ich wartości. W tym przypadku "lokalne" oznacza zmienne zdefiniowane w aktualnym zasięgu. Poprzedni kod można by zapisać następująco: def current_datetime(request):
current_date = datetime.datetime.now()
return render_to_response('current_datetime.html', locals())
Tym razem zamiast ręcznie określać słownik kontekstu, przekazujemy wynik locals(), który zawiera wszystkie zmienne zdefiniowane do tej pory w funkcji. Zmieniliśmy nazwę definiowanej zmiennej na current_date, ponieważ takiej nazwy oczekuje szablon. W tym konkretnym przypadku użycie locals() nie zaowocowało znaczącą poprawą, ale ta sztuczka potrafi zaoszczędzić nieco pisania przy większej liczbie zmiennych — szczególnie przy chronicznym lenistwie. Stosując funkcję locals() warto pamiętać, że dołącza ona wszystkie zmienne lokalne, co może spowodować przekazywanie większej ilości danych niż potrzeba. W poprzednim przykładzie locals() zwróci również zmienną request. Czy jest to pożądane, zależy od aplikacji i założonego poziomu perfekcjonizmu. Podfoldery w get_template()Przechowywanie wszystkich szablonów w jednym folderze byłoby wysoce nieefektywne. Nic nie stoi na przeszkodzie, by zacząć przechowywać szablony w podfolderach głównego szablonu. W zasadzie zalecamy takie rozwiązanie. W zasadzie niektóre bardziej zaawansowane elementy Django (system uogólnionych widoków opisywany w rozdziale 9.) zakłada stosowanie przez szablony właśnie takiej konwencji. Przechowywanie szablonów w podfolderach folderu template jest proste. W trakcie wywoływania funkcji get_template() wystarczy podać nazwę podfolderu i ukośnik przed nazwą szablonu: t = get_template('dateapp/current_datetime.html')
Ponieważ render_to_response() stanowi tylko niewielką otoczkę wokół get_template(), można tę samą sztuczkę wykonać z pierwszym argumentem render_to_response(): return render_to_response('dateapp/current_datetime.html', {'current_date': now})
Nie istnieje żadne ograniczenie co do głębokości poddrzew. Stosuj dowolne podfoldery w dowolnej ilości. Uwaga Użytkownicy systemu Windows powinni pamiętać o stosowaniu ukośników zamiast lewych ukośników. Funkcja get_template() zakłada stosowanie separatorów w stylu Uniksowym. Znacznik szablonowy includePo omówieniu systemu wczytywania szablonów możemy wprowadzić wbudowany znacznik szablonowy, który z niego korzysta: znacznik {% include %}. Znacznik ten umożliwia dołączenie zawartości innego szablonu. Argumentem jest nazwa szablonu, który należy dołączyć. Argument może być stały (ciąg znaków w cudzysłowach lub apostrofach) lub być zmienną. Jeśli ten sam fragment kodu szablonu pojawia się w kilku szablonach, zastanów się nad użyciem ``{% include %} w celu pozbycia się duplikatów. Dwa poniższe przykłady dołączają zawartość szablonu nav.html. Przykłady są sobie równoznaczne i pokazują, że rodzaj znaczników nie ma żadnego znaczenia: {% include 'nav.html' %}
{% include "nav.html" %}
Poniższy przykład wstawia zawartość szablonu includes/nav.html: {% include 'includes/nav.html' %}
Poniższy przykład wstawia zawartość szablonu, którego nazwa została umieszczona w zmiennej template_name: {% include template_name %}
Podobnie jak w przypadku get_template() nazwa pliku z szablonem powstaje na podstawie połączenia folderu zawartego w TEMPLATE_DIRS i podanej jako parametr nazwy. Dołączane szablony są wykonywane w kontekście szablonu, który je wstawił. Rozważmy te dwa przykłady: # mypage.html
<html>
<body>
{% include "includes/nav.html" %}
<h1>{{ title }}</h1>
</body>
</html>
# includes/nav.html
<div id="nav">
Znajdujesz się w {{ current_section }}
</div>
Jeśli zrenderujesz mypage.html z kontekstem zawierającym current_section, wtedy zmienna ta będzie dostępna w dołączonym szablonie tak, jakbyś tego oczekiwał. Jeśli jednak szablon dołączany za pomocą {% include %} nie odnajdzie wymaganej zmiennej, Django wykona jedną z dwóch operacji:
Dziedziczenie szablonówDo tej pory wszystkie przykłady były bardzo małe, ale w rzeczywistości system szablonów Django ma służyć do tworzenia bardzo rozbudowanych stron HTML. Prowadzi to do powstania typowego problemu: jak na poziomie całej witryny zredukować duplikację podobnych obszarów kodu, na przykład paska nawigacyjnego? Klasycznym sposobem rozwiązania tego problemu jest użycie dołączeń na poziomie serwera, specjalnych dyrektyw powodujących dołączenie jednej strony wewnątrz innej. Django również obsługuje to podejście dzięki opisanemu wcześniej znacznikowi {% include %}. Jednak preferowanym w Django i bardziej eleganckim sposobem rozwiązania tego problemu jest dziedziczenie szablonów. Dziedziczenie szablonów pozwala stworzyć bazowy "szkielet" szablonu, który zawiera wszystkie podstawowe elementy szablonu oraz dodatkowe bloki, w których swoją treść mogą umieszczać szablony potomne. Sprawdźmy ten system w działaniu, tworząc bardziej złożony szablon dla widoku current_datetime, edytując plik current_datetime.html: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="pl">
<head>
<title>Aktualny czas</title>
</head>
<body>
<h1>Moja pomocna zegarynka</h1>
<p>Teraz jest {{ current_date }}.</p>
<hr>
<p>Dziękuję za odwiedziny.</p>
</body>
</html>
Wszystko wygląda poprawnie, ale co się stanie, jeśli będziemy chcieli utworzyć szablon dla innego widoku — na przykład widoku hours_ahead z rozdziału 3. Gdyby nowy szablon również miał być w pełni poprawnym szablonem HTML, napisalibyśmy następujący kod: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="pl">
<head>
<title>Czas przyszły</title>
</head>
<body>
<h1>Moja pomocna zegarynka</h1>
<p>Za {{ hour_offset }} godzin będzie {{ next_time }}.</p>
<hr>
<p>Dziękuję za odwiedziny.</p>
</body>
</html>
Widać wyraźnie, że powieliliśmy mnóstwo kodu HTML. Wyobraź sobie typową witrynę z paskiem nawigacyjnym, kilkoma arkuszami stylów, skryptami JavaScript — ilość powielanego kodu byłaby w tym przypadku ogromna. W rozwiązaniu stosującym dołączenia po stronie serwera wydobywa się podobne fragmenty z obu szablonów i umieszcza w osobnych plikach dołączanych później do obu szablonów. Być może nagłówek umieścilibyśmy w pliku o nazwie header.html: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"> <html lang="pl"> <head> Stopkę zapewne umieścilibyśmy w pliku footer.html: <hr>
<p>Dziękuję za odwiedziny.</p>
</body>
</html>
W systemie bazującym na dołączeniach nagłówek i stopka są proste. To reszta wygląda mniej przyjaźnie. Obie strony zawierają znacznik tytułu — <h1>Moja pomocna zegarynka</h1> — ale nie można go umieścić w pliku header.html, ponieważ znacznik <title> zawiera inną treść. Gdybyśmy chcieli umieścić <h1> musimy również umieścić <title> co uniemożliwiłoby stosowanie różnych tytułów na poszczególnych stronach. Czy widzisz dokąd zmierzamy? System dziedziczenia szablonów rozwiązuje ten problem. Można go traktować jako "odwrócony" system dołączeń po stronie serwera. Zamiast definiować fragmenty, które są wspólne, definiuje się fragmenty, które są różne. Pierwszy krok polega na definicji szablonu bazowego — szkieletu strony, który później wypełnią szablony potomne. Oto szablon bazowy dla naszego przykładu: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="pl">
<head>
<title>{% block title %}{% endblock %}</title>
</head>
<body>
<h1>Moja pomocna zegarynka</h1>
{% block content %}{% endblock %}
{% block footer %}
<hr>
<p>Dziękuję za odwiedziny.</p>
{% endblock %}
</body>
</html>
Ten podstawowy szablon, który nazwiemy base.html, definiuje prosty szablon HTML wykorzystywany dla wszystkich stron witryny. To szablony potomne odpowiadają za nadpisanie, dodanie lub pozostawienie zawartości aktualnych bloków. (Jeśli wykonujesz ten przykład, zapisz szablon w pliku base.html w folderze template). W przykładzie użyliśmy nieopisywany do tej pory znacznik: {% block %}. Wszystko, co czyni ten znacznik, to informowanie systemu szablonów, że szablony potomne mogą nadpisać wskazaną zawartość. Skoro mamy już szablon bazowy, możemy zmodyfikować istniejący szablon current_datetime.html, by z niego korzystał: {% extends "base.html" %}
{% block title %}Aktualny czas{% endblock %}
{% block content %}
<p>Teraz jest {{ current_date }}.</p>
{% endblock %}
Dodatkowo wykonajmy szablon dla widoku hours_ahead z rozdziału 3. (Zmianę kodu widoku, by używał szablonu zamiast zakodowanej na stałe treści pozostawiamy jako ćwiczenie). Oto jak mógłby wyglądać taki szablon: {% extends "base.html" %}
{% block title %}Czas przyszły{% endblock %}
{% block content %}
<p>Za {{ hour_offset }} godzin będzie {{ next_time }}.</p>
{% endblock %}
Czy to nie piękne? Każdy szablon zawiera tylko kod, który jest unikatowy dla niego. Nie ma żadnej redundancji. Jeśli konieczne są zmiany na poziomie całej witryny, wystarczy zmienić plik base.html. Wszystkie pozostałe szablony automatycznie zauważą zmianę. Oto jak działa cały system. W momencie wczytywania szablonu current_datetime.html system widzi znacznik {% extends %} informujący, że jest to szablon potomny. System automatycznie wczytuje szablon bazowy — w tym przypadku base.html. W tym momencie system szablonów zauważa trzy znaczniki {% block %} w pliku base.html i zastępuje je zawartością, która pojawiła się w szablonie potomnym. Oznacza to, że użyje zawartości bloków {% block title %} i {% block content %} z szablonu potomnego. Zauważ, że szablon potomny nie przedefiniował bloku stopki, więc wynikową wartością pozostała wartość zdefiniowana w szablonie bazowym. Zawartość znacznika {% block %} z szablonu bazowego zawsze stanowi wartość rezerwową. Dziedziczenie nie wpływa na kontekst szablonu. Innymi słowy, dowolny szablo z drzewa dziedziczenia będzie miał dostęp do wszystkich zmiennych kontekstu. Można stosować dowolną liczbę poziomów dziedziczenia. Najczęściej jednak stosuje się podejście trójpoziomowe:
To podejście maksymalizuje ponowne użycie kodu i ułatwia dodanie elementów do współdzielonych obszarów, na przykład nawigacji dotyczącej określonej sekcji. Oto kilka dodatkowych rad dotyczących korzystania z dziedziczenia szablonów:
Co dalej?Gdy poznałeś już podstawy systemu szablonów Django, co dalej? Wiele nowoczesnych witryn internetowych korzysta z baz danych: zawartość witryny znajduje się relacyjnej bazie danych. Umożliwia to dobrą separację danych i logiki (podobnie jak widoki i szablony zapewniają separację logiki i prezentacji). Następny rozdział omawia narzędzia Django związane z obsługa baz danych. |
Rysunek 4-1. Strona błędu wskazująca niemożność odnalezienia pliku szablonu