Django: Zpracovávání formulářů

Pomocí formulářů uživatelé zadávají data do aplikace. Manuální zpracovávání formulářů ale bývá pro programátory hodně otravné. Django naštěstí umožňuje formuláře generovat a zpracovávat, což programátorům usnadňuje život. V článku se na zpracování formulářů v Djangu podíváme podrobněji.

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

Práce s formulářem by měla být pro uživatele co nejpříjemnější. Doporučuje se proto nepřehánět stylování prvků formuláře, aby to nevedlo ke zmatení. Ideální je ponechat výchozí styl systému. Také by se uživateli při špatně zadaném vstupu mělo zobrazit, kde přesně udělal chybu a jak ji má napravit. V takovém případě nesmí dojít k vymazání zadaných hodnot z formuláře.

Kontaktní formulář

Po několika letech vytváření webů jsem si všiml, že zadavatelé firemních webových stránek mají v oblibě kontaktní formuláře. Do takového formuláře návštěvník vyplní své osobní údaje a požadavek. Po zpracování formuláře se odešle jeho obsah e-mailem. Osobně jsem smysl této funkce moc nepochopil, ale na ukázku práce s formuláři se nám to hodí.

Specifikace formulářů v Djangu se v mnohém podobá databázovým modelům. Později si předvedeme, jak z modelu automaticky generovat formuláře, ale zatím nám bude stačit vytvořit jednoduchý formulář. Vytvoříme si nový soubor video_store/forms.py a do něj napíšeme následující kód:

# coding: utf-8
from django import forms

class ContactForm(forms.Form):
    name = forms.CharField(label='Jméno', max_length=100, initial='Anonym')
    email = forms.EmailField(label='E-mail', required=False)
    phone = forms.CharField(label='Telefon', max_length=20, required=False)
    reaction = forms.BooleanField(label='Chci dostat odpověď', required=False)
    text = forms.CharField(label='Zpráva', widget=forms.Textarea, help_text='Sem napište svou připomínku nebo dotaz.')

Můžeme si všimnout, že jsme odvodili náš formulář z třídy django.forms.Form. Atributy této nové třídy se podobají atributům databázových modelů, jenom používají trochu jiné parametry. Místo parametru verbose_name, který určuje popisek atributu, tu máme parametr label. Parametr max_length má stejný význam jako u modelů — je to nejvyšší povolený počet znaků, který uživatel může zadat. Také parametr initial se podobá modelovému parametru default, jedná se tedy o předvyplněnou (výchozí) hodnotu atributu. Parametr required má opačný význam než parametr blank, vyskytující se u modelů. Pokud se mu přiřadí hodnota False, znamená to, že je atribut nepovinný, což u modelů představuju hodnote True. Parametr help_text je nápovědný text, který se uživateli zobrazí u políčka formuláře. A parametr widget určuje použitý formulářový prvek. V tomto případě se místo výchozí značky <input type="text"> při generování použije značka <textarea>. Oficiální dokumentace obsahuje přehled všech formulářových prvků a všech druhů formulářových políček.

Django 8

Když jsme si určili formulář, můžeme si zkusit z něj vygenerovat příslušný HTML kód. Existuje několik metod, které to zvládnou (z důvodů zachování přehlednosti je výpis zkrácen):

>>> from hrajeme_si.video_store.forms import ContactForm
>>> form = ContactForm()
>>> form.as_p()
u'<p><label for="id_name">Jméno:</label> <input id="id_name" type="text" name="name" value="Anonym" maxlength="100" /></p>…'
>>> form.as_ul()
u'<li><label for="id_name">Jméno:</label> <input id="id_name" type="text" name="name" value="Anonym" maxlength="100" /></li>…'
>>> form.as_table()
u'<tr><th><label for="id_name">Jméno:</label></th><td><input id="id_name" type="text" name="name" value="Anonym" maxlength="100" /></td></tr>…'

Tyto tři metody umí vytvořit formulář pomocí odstavců, seznamu a tabulky. Stačilo by příslušnou metodu zavolat na vhodném místě v šabloně (to se dělá např. použitím značky {{ form.as_table }}). Ale co když požadujeme větší kontrolu nad výstupním kódem? Můžeme si vytvořit vlastní šablonu, která bude pomocí cyklu for postupně procházet jednotlivá formulářová políčka a určovat výstupní formát. Takovou šablonu si napíšeme a uložíme do souboru templates/form.html:

{% for f in form %}
  {% if f.errors %}
  <tr>
  <td colspan="2">{{ f.errors }}</td>
  </tr>
  {% endif %}

  <tr>
  <th{% if f.field.required %} class="required"{% endif %}>
  <label for="id_{{ f.name }}">{{ f.label }}</label>
  </th>
  <td>{{ f }}</td>
  </tr>

  {% if f.help_text %}
  <tr>
  <td></td>
  <td class="help_text">{{ f.help_text }}</td>
  </tr>
  {% endif %}
{% endfor %}

Výstup je podobný metodě as_table, ale máme možnost ho v případě potřeby jednoduše měnit. Na druhém až šestém řádku zobrazíme uživateli chybové hlášky, pokud vyplnil do formuláře něco špatně. Osmý až třináctý řádek generuje samotnou kolonku formuláře i s popiskem. Jestliže je popisek potřeba vyplnit, aplikuje se na něj kaskádová třída required, což uživateli může pomoct s orientací ve formuláři. Patnáctý až dvacátý řádek se postará o výpis případného nápovědného textu.

Django 8

Tato šablona je univerzální, takže ji můžeme použít i v našem kontaktním formuláři. Je potřeba vytvořit další šablonu (templates/contact.html), která bude pomocí značky {% include %} využívat formulářovou šablonu:

{% extends "base.html" %}

{% block content %}
<h1>{% block title %}Kontakt{% endblock %}</h1>

{% if sent %}
  <p>Vaše zpráva byla odeslána. Děkujeme!</p>
{% else %}
  <form action="." method="POST">
  <table>
  {% include "form.html" %}
  <tr>
  <td></td>
  <td><input type="submit" value="Odeslat"></td>
  </tr>
  </table>
  </form>
{% endif %}
{% endblock %}

V této šabloně se podle hodnoty proměnné sent uživateli zobrazí potvrzující hláška nebo formulář. Hodnota se totiž v konstrukci if vyhodnotí jako True, pokud obsahuje nějaký neprázdný (nenulový) objekt. Může to být například neprázdný textový řetězec.

Kromě šablon potřebujeme i příslušný pohled, který formulář zpracuje a pokusí se poslat e-mail. Přidáme ho na konec souboru video_store/views.py:

from forms import ContactForm
from django.core.mail import mail_managers

def contact(request, sent):
    if request.method == 'POST':
        form = ContactForm(request.POST)

        if form.is_valid():
            message = u'Uživatel vyplnil následující údaje:nn'

            for item in form.fields.keyOrder:
                if len(unicode(form.cleaned_data[item])) > 0:
                    message += form[item].label + u': ' + unicode(form.cleaned_data[item]) + u'n'

            mail_managers('Nová zpráva z kontaktního formuláře', message)

            return HttpResponseRedirect('/kontakt/odeslano/')
    else:
        form = ContactForm()

    return render_to_response('contact.html', {'form': form, 'sent': sent}, context_instance=RequestContext(request))

Druhým argumentem pohledu je proměnná sent. Ta se pouze předá šabloně formuláře a podle jejího obsahu se určí, co se má zobrazit. Formulář je zasílán metodou POST, takže se dá lehce poznat, zda už byl vyplněn. To rozhodujeme podle objektu request (řádek 5). Pokud nebyl vyplněn, vypíšeme pouze prázdný formulář (řádek 19). V opačném případě vložíme do formuláře zadaná data (řádek 6) a zjistíme, jestli je vstup validní (řádek 8). Kupříkladu jestli e-mail vyhovuje určitému regulárnímu výrazu nebo jestli jsou vyplněny všechny povinné položky. Pokud je všechno v pořádku, sestavíme text e-mailu a rozešleme jej lidem, kteří jsou zodpovědní za správu stránky. Po úspěšném odeslání e-mailu dotaz přesměrujeme na stránku s hláškou, abychom předešli případům, kdy se při náhodném obnovení stránky formulář zpracovává znovu.

Pozor, v případě sestavování textu e-mailu se musí použít tzv. unicode řetězec (má před sebou písmeno u), jinak může dojít při zadání diakritického textu k vyhození výjimky UnicodeError (selhání při dekódování znakové sady).

Ještě než nastavíme URL, měli bychom určit e-mailové adresy, na které se bude obsah kontaktního formuláře rozesílat. Stačí upravit konstantu MANAGERS v souboru settings.py například takto:

MANAGERS = (
    ('Jan Novák', 'honza@example.cz'),
    ('Franta Uživatel', 'franta@example.cz'),
)

Pokud se e-mailový server nenachází na počítači, na kterém aplikaci provozujeme, musíme ve stejném souboru přenastavit konstanty začínající prefixem EMAIL, jinak nám rozesílání e-mailů nebude fungovat. Bez e-mailového serveru lze skript také testovat, jenom je potřeba funkci mail_managers volat s parametrem fail_silently=True, aby se potlačila chybová hláška.

A konečně, teď jenom stačí přidat správný řádek do proměnné urlpatterns v souboru urls.py (z důvodů přehlednosti opět zkráceno):

urlpatterns = patterns('hrajeme_si',
    # ...
    (r'^kontakt(|/odeslano)/$', 'video_store.views.contact'),
)

Regulární výraz (|/odeslano) přiřadí proměnné sent buď prázdnou hodnotu nebo řetězec '/odeslano', který stačí k vyvolání hlášky o odeslání.

Django 8

Související odkazy

Příště se dozvíme něco o autentizaci a autorizaci uživatelů v Djangu.

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: 5

Přehled komentářů

Honza Kral unicode a prace se stringy
Pavel Dvořák Re: unicode a prace se stringy
Tomaasch Obecnejsi kod
Pavel Dvořák Re: Obecnejsi kod
Pavel Dvořák Oprava kódu sestavujícího e-mailovou zprávu
Zdroj: https://www.zdrojak.cz/?p=3091