Django: Rozšiřování možností Djanga

Holá instalace Djanga má mnoho různých funkcí. Určitě však časem zjistíme, že nám nějaká maličkost chybí. Proto se v tomto díle podíváme na několik ukázek možného rozšíření.
Seriál: Hrajeme si s Djangem (16 dílů)
- Django: Úvod a instalace 14. 8. 2009
- Django: Nastavení projektu a první pokusy 21. 8. 2009
- Django: Databázový model 28. 8. 2009
- Django: Databázový model podruhé 4. 9. 2009
- Django: Administrace 11. 9. 2009
- Django: Prezentace dat 18. 9. 2009
- Django: Prezentace dat podruhé 25. 9. 2009
- Django: Zpracovávání formulářů 2. 10. 2009
- Django: Autentizace a autorizace 9. 10. 2009
- Django: Nahrávání souborů 16. 10. 2009
- Django: Zabudované aplikace 23. 10. 2009
- Django: Rozšiřování možností Djanga 30. 10. 2009
- Django: Internacionalizace 6. 11. 2009
- Django: Nasazování projektu 13. 11. 2009
- Django: Kešování a škálování 20. 11. 2009
- Django: Závěr 27. 11. 2009
Přidání vlastního filtru do šablony
Určitě si vzpomínáte na šestý díl seriálu, kde jsme se učili prezentovat data pomocí generických pohledů. V příkladu jsme si vypsali mimo jiné e-mailovou adresu provozovny. Tyto adresy nejsou nijak chráněné, takže je může zaznamenat i ten nejhloupější spambot, což představuje problém. Proto si napíšeme jednoduchý filtr, pomocí kterého e-mailovou adresu transformujeme do zakódovaného řetězce v JavaScriptu.
Filtr je vlastně obyčejná funkce, které předáme nějaký vstup a ona vrátí odpovídající výstup. Každá aplikace, která je uvedená v konstantě INSTALLED_APPS
v souboru settings.py
, může mít svou sadu filtrů a značek. Stačí v adresáři aplikace (v našem případě video_store
) vytvořit podadresář templatetags
a umístit do něj dva soubory. První je potřeba pojmenovat __init__.py
a bude stačit, když bude prázdný. Označuje totiž, že daný adresář obsahuje modul napsaný v Pythonu. Druhý soubor můžeme pojmenovat libovolně, já si zvolil název misc.py
, protože do tohoto souboru potom možná časem přidám další různorodé funkce (anglicky miscellaneous). Obsah souboru vypadá takto:
# coding: utf-8 from base64 import b64encode from django import template from django.utils.safestring import mark_safe from django.template.defaultfilters import stringfilter register = template.Library() @register.filter @stringfilter def email(value): return mark_safe('<script type="text/javascript">display_email("%s");</script><noscript>(e-mail je chráněn JavaScriptem)</noscript>' % b64encode(value))
Na sedmém řádku se nachází přiřazení, které určuje, že se jedná o knihovnu filtrů a značek. Proměnná se musí jmenovat register
a musí obsahovat objekt template.Library
— takto to stanovuje konvence. Na devátém a desátém řádku můžeme vidět dva dekorátory. První z nich (@register.filter
) registruje nový filtr. Kdybychom chtěli přidat novou značku, napíšeme místo toho @register.tag
. Tento dekorátor má nepovinný parametr name
, jež určuje název nového filtru či značky. My jsme ho vynechali, takže se název filtru odvodí z názvu funkce. Druhý dekorátor dbá na to, aby funkce dostala jako vstup řetězec. V případě, kdy by filtr byl připojen např. k číslu, by při zpracování šablony došlo k vyhození výjimky AttributeError
. Tento dekorátor také převádí objekty na řetězce voláním metod __str__
nebo __unicode__
.
Samotný filtr vezme hodnotu a vrátí HTML kód s JavaScriptem. Funkce mark_safe
označí řetězec za bezpečný. Nedojde tak k automatickému escapování HTML značek a entit, takže se kupříkladu místo kódu <noscript>
vypíše očekávaná hodnota <noscript>
. Tuto vlastnost vypínejte s rozmyslem, hlavně při vypisování uživatelem zadaných dat. Pokud byste chtěli ošetřit uživatelem zadaná data sami, můžete k tomuto účelu použít funkci escape
z modulu django.utils.html
. V našem případě zaručíme bezpečnost vstupu tím, že ho zakódujeme pomocí kódování base64, které obsahuje pouze písmena abecedy, číslice a znaky plus, lomítko a rovnítko (viz RFC 3548). Takto zakódovaný e-mail spambot rozezná těžko.
Dále nesmíme zapomenout upravit šablonu s výpisem poboček (templates/stores.html
):
{% extends "base.html" %} {% load misc %} {% 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>{{ object.email|email }}</li> {% endif %} </ul> {% if object.description %} <p>{{ object.description }}</p> {% endif %} {% endfor %} {% endblock %}
V tomto souboru přibyla na druhém řádku značka pro načtení našeho modulu s filtrem misc.py
. Na patnáctém řádku se e-mail nevypisuje přímo, ale přes filtr email
což zatím nebude fungovat, protože nemáme definovanou javascriptovou funkci display_email
. Přesto se ale můžeme podívat na to, co uvidí uživatelé s vypnutým JavaScriptem.
Založíme si tedy někde v adresáři static
javascriptový soubor, který poté načteme přes značku <script>
v šabloně templates/base.html
. Tento soubor bude obsahovat již zmiňovanou funkci pro výpis e-mailu:
function display_email(str) { email = b64decode(str); document.write('<a href="mailto:' + encodeURI(email) + '">' + email + '</a>'); }
Na druhém řádku se volá funkce b64decode
pro převedení zakódovaného řetězce do původní podoby. Ta v JavaScriptu není standardně implementována, takže jsem ji si musel napsat sám. Protože je však celkem rozsáhlá a pro další vysvětlování není potřeba, případní zájemci se na ni mohou podívat v přiloženém ukázkovém příkladu (soubor static/js/scripts.js
). Co se týče čtvrtého řádku této funkce, ten zobrazí uživateli klikatelný e-mail.
Přidání proměnné do kontextu
Opět navážeme na poznámku ze šestého dílu seriálu, kde jsme se zmínili o kontextových procesorech. Díky nim můžeme v šablonách používat společné proměnné (což je např. {{ MEDIA_URL }}
nebo {{ user }}
), stačí vždy při volání metody render_to_response
uvést parametr context_instance=RequestContext(request)
. Konstanta TEMPLATE_CONTEXT_PROCESSORS určuje, které kontextové procesory se v projektu použijí. V dokumentaci Djanga je popsáno několik základních procesorů, my si napíšeme vlastní.
Zadavatel našeho projektu dostal šílený nápad, že by se v hlavičce webu měla zobrazovat náhodně některá z předem připravených marketingových hlášek. Protože se tato funkce týká všech pohledů, při implementaci přímo v pohledech, by došlo k zbytečnému duplikování kódu. K tomuto účelu je lepší vybranou hlášku předat šabloně jako globální kontext. Náhodné vypisování by se to sice dalo vyřešit jednoduchým kódem v JavaScriptu, ale myslím, že skriptování v prohlížeči už dnes bylo dost.
Soubor s funkcí, která přidává další kontext, si můžeme založit kdekoliv. Například přímo v adresáři projektu. Pojmenujeme ho jako context_processors.py
:
# coding: utf-8 from random import choice def nonsense(request): quotes = [ 'Jsme nejlepší!', 'Naše tituly do vašeho přehrávače.', 'Dobrý den je, když si pustíte film.', 'Je přece správné platit za půjčování.', 'Tohle u konkurence nenajdete.', ] return {'NONSENSE': choice(quotes)}
Funkce pro přidání kontextu požaduje jako parametr objekt Request a vrací slovník s deklaracemi proměnných. Bývá zvykem proměnné z kontextového procesoru zapisovat verzálkami, aby se odlišily od těch z pohledů. V naší funkci náhodně vybíráme jeden z blábolů, ke kterému poté budeme mít možnost přistupovat pomocí proměnné {{ NONSENSE }}
.
Tento kontextový procesor je potřeba přidat do nastavení. Otevřeme si tedy soubor settings.py
a dopíšeme do něj následující řádky:
TEMPLATE_CONTEXT_PROCESSORS = ( 'django.core.context_processors.auth', 'django.core.context_processors.debug', 'django.core.context_processors.i18n', 'django.core.context_processors.media', 'hrajeme_si.context_processors.nonsense', )
První čtyři procesory jsou výchozí, ten poslední je náš. Teď už stačí jenom upravit v hlavní šabloně templates/base.html
horní část webu:
<div id="header"> Videopůjčovny s. r. o. {% if NONSENSE %} <div id="nonsense">{{ NONSENSE }}</div> {% endif %} </div>
Související odkazy
- Vlastní filtry a značky a šablony pro programátory na Djangoproject.com
- Devátá kapitola v The Definitive Guide to Django
- Ukázkový projekt ke stažení.
Příště se podíváme na tvorbu vícejazyčných webů.