Přejít k navigační liště

Zdroják » Různé » Django: Zpracovávání formulářů

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

Články Různé

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.

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.

Komentáře

Subscribe
Upozornit na
guest
5 Komentářů
Nejstarší
Nejnovější Most Voted
Inline Feedbacks
View all comments
Honza Kral

Prosim vyhnete se pouzivanim bytestringu (str()) v djangu, zejmena v kombinaci s unicode stringy. Vede to k osklivym errorum ktere jsou tezko odhalitelne protoze se vyskytuji jen nekdy, vsude pouzivejte unicode.

Rovnez opetovne scitani stringu (byte i unicode) je vhodne se vyhnout, je to neumerne draha operace:

out = [message]
for item in ['name', 'email', 'phone', 'reaction', 'text']:
  if unicode(form.cleaned_data[item]):
    out.extend([form[item].label, u': ', unicode(form.cleaned_data[item]), u'n'])
message = ''.join(out)

Nehlede na to, ze pokud mym zamerem je jen odeslat data v emailu, neni vhodne pozadovat po Djangu aby vstupy z formulare prevadelo na datove typy pythonu (Boolean) jen proto abych je pak prevedl zpet, ChoiceField by v tomto pripade vyhovoval mnohem vic a usetril by dost kodu (vsechny prevody na unicode v zobrazenem view).

Tomaasch

Kod smycky pro vypis fieldu jde pouzit o neco obecneji, misto

    for item in ['name', 'email', 'phone', 'reaction', 'text']:

je IMHO lepsi pouzit ponekud obecnejsi

    for item in form.fields.keyOrder:

Pak muzeme vesele pridavat dalsi fieldy pouze do modelu a view pritom zustane netknute…

Enum a statická analýza kódu

Mám jednu univerzální radu pro začínající programátorty. V učení sice neexistují rychlé zkratky, ovšem tuhle radu můžete snadno začít používat a zrychlit tak tempo učení. Tou tajemnou ingrediencí je statická analýza kódu. Ukážeme si to na příkladu enum.