Formuláre
Formuláre sú pre webovská aplikácie veľmi dôležité. V klasickom ponímaní je to jediný spôsob ako môžu užívatelia zaslať http serveru dáta.
Typické začiatočnícke chyby
Programovanie obsluhy formulárov je náchylné na chyby, napríklad:
- Programátor predpokladá, že keď prídu dáta tak skutočne pochádzajú z formulára. Napríklad ak je vo formulári voľba muž/žena, kód pre spracovanie zaslaných dát predpokladá, že tam nemôže byť iný reťazec, napríklad marťan.
- Programátor predpokladá, reťazce sú nanajvýš tak dlhé, ako vyžadoval vo formulári. Vedie k preťaženiu servra.
- Programátor predpokladá, že zadané reťazce neobsahujú žiadne špeciálne znaky. Toto vedie k dvom typom chýb:
SQL injection. Vezmeme reťazec zadaný užívateľom a bez akejkoľvek kontroly z neho vyrobíme SQL reťazec, ktorý pošleme databázovému servru. Zlý užívateľ nám pošle napr. a';DROP TABLE users; SELECT * FROM data WHERE name LIKE '% a je zle. Toto je bohužiaľ ešte stále dosť rozšírená fatálna chyba webových aplikácií.
Cross-site scripting. Vezmeme reťazec zadaný užívateľom a bez akejkoľvek kontroly ho zapíšeme do databázy a potom zobrazíme na stránke. Toto je veľmi časté. Problém je v tom, že tento reťazec môže obsahovať JavaScript, ktorý môže so stránkou urobiť čokoľvek, napríklad zmeniť formulár pre zadávanie hesla tak, aby ho poslal nič netušiaci užívateľ útočníkovi.
Bod 3a sa rieši v djangu tým, že používame modely.
Bod 3b je vyriešený, ak dôsledne používame šablóny -- t.j. žiadne explicitné HTML vo views. Template.render totiž všetko HTML ktoré prípadne obsahuje Context prerobí na neškodný plochý text.
Ochranu pred chybami spomenutými v bodoch 1. a 2. pre nás django zabezpečuje prostredníctvom triedy Form
Trieda Form
Formuláre v djangu vyžívajú sebareflexívne vlastnosti Pythonu.
Neviazané formuláre
1 $ ./manage.py shell
2 Python 2.5.2 (r252:60911, Jan 4 2009, 17:40:26)
3 [GCC 4.3.2] on linux2
4 Type "help", "copyright", "credits" or "license" for more information.
5 (InteractiveConsole)
6 >>> from django import forms
7 >>> class MyForm(forms.Form):
8 ... meno=forms.CharField(max_length=20)
9 ... priezvisko=forms.CharField(max_length=20,required=True)
10 ... znamka=forms.IntegerField(min_value=1,max_value=5,required=True)
11 ... protekcny_ziak=forms.BooleanField()
12 ...
13 >>> form=MyForm()
Vytvorili sme neviazanú inštanciu triedy Form. Trieda Form implementuje metódu __repr__:
>>> print form <tr><th><label for="id_meno">Meno:</label></th><td><input id="id_meno" type="text" name="meno" maxlength="20" /></td></tr> <tr><th><label for="id_priezvisko">Priezvisko:</label></th><td><input id="id_priezvisko" type="text" name="priezvisko" maxlength="20" /></td></tr> <tr><th><label for="id_znamka">Znamka:</label></th><td><input type="text" name="znamka" id="id_znamka" /></td></tr> <tr><th><label for="id_protekcny_ziak">Protekcny ziak:</label></th><td><input type="checkbox" name="protekcny_ziak" id="id_protekcny_ziak" /></td></tr>
to znamená, že v šablóne môžeme použiť {{ form }} .
V browseri to potom vyzerá takto:
Všimnite si, že tam nie je odosielací button; ten patrí do šablóny.
Vykresľovať formulár ako tabuľku nie je jediná možnosť:
1 >>> print form.as_ul()
2 <li><label for="id_meno">Meno:</label> <input id="id_meno" type="text" name="meno" maxlength="20" /></li>
3 <li><label for="id_priezvisko">Priezvisko:</label> <input id="id_priezvisko" type="text" name="priezvisko" maxlength="20" /></li>
4 <li><label for="id_znamka">Znamka:</label> <input type="text" name="znamka" id="id_znamka" /></li>
5 <li><label for="id_protekcny_ziak">Protekcny ziak:</label> <input type="checkbox" name="protekcny_ziak" id="id_protekcny_ziak" /></li>
vyzerá takto:
alebo
1 >>> print form.as_p()
2 <p><label for="id_meno">Meno:</label> <input id="id_meno" type="text" name="meno" maxlength="20" /></p>
3 <p><label for="id_priezvisko">Priezvisko:</label> <input id="id_priezvisko" type="text" name="priezvisko" maxlength="20" /></p>
4 <p><label for="id_znamka">Znamka:</label> <input type="text" name="znamka" id="id_znamka" /></p>
5 <p><label for="id_protekcny_ziak">Protekcny ziak:</label> <input type="checkbox" name="protekcny_ziak" id="id_protekcny_ziak" /></p>
vyzerá takto
Ak sa to niekomu nepáči, môže pokojne použiť CSS aby si veci zafarbil podľa svojho gusta.
Viazané formuláre
Viazaný formulár vznikne tak, že sa skonštruuje zo slovníka zobrazujúceho reťazce na reťazce. V praxi sú to užívateľom zaslané dáta.
1 >>> form=MyForm({'meno':'Jozef','priezvisko':'Mak','znamka':'3','protekcny_ziak':'1'})
Jednoduchým spôsobom môžeme skontrolovať, či sú dáta v poriadku.
Vo form.cleaned_data máme teraz k dispozícii slovník s dátami.
Všimnite si, že dáta už sú správneho typu:
Teraz vyskúšame, čo urobia chybné dáta. Ako znamka pošleme nečíslo.
To sa dalo čakať.
Čo ale teraz? Mali by sme byť schopní zistiť, aká je chyba v zaslaných dátach a požadovať od užívateľa nápravu. Django to ale zabezpečí za nás:
1 >>> print form
2 <tr><th><label for="id_meno">Meno:</label></th><td><input id="id_meno" type="text" name="meno" value="Jozef" maxlength="20" /></td></tr>
3 <tr><th><label for="id_priezvisko">Priezvisko:</label></th><td><input id="id_priezvisko" type="text" name="priezvisko" value="Mak" maxlength="20" /></td></tr>
4 <tr><th><label for="id_znamka">Znamka:</label></th><td><ul class="errorlist"><li>Enter a whole number.</li></ul><input type="text" name="znamka" value="xxx" id="id_znamka" /></td></tr>
5 <tr><th><label for="id_protekcny_ziak">Protekcny ziak:</label></th><td><input checked="checked" type="checkbox" name="protekcny_ziak" value="1" id="id_protekcny_ziak" /></td></tr>
6 >>>
Vyzerá takto:
| |
- Užívateľom zaslané dáta sú predvyplnené.
Django zahlásil chybu v angličine, lokalizovať chybové hlášky do iného jazyka je možné pomocou nastavenia v settings.py.
V HTML vidno, že zoznam chýb má triedu errorlist. V CSS môžeme tejto triede nastaviť atribúty podľa ľubovôle.
V praxi nás nemusí zaujímať, aká je chyba vo formulári. Stačí sa pozrieť na form.is_valid() a ak nevráti True, zobraziť formulár znovu.
Skúsme ešte narobiť trchu viac a iných chýb:
1 >>> form=MyForm({'meno':'Jozefxxxxxxxxxxxxxxxxxxxxxxxxxxxx','znamka':'10','protekcny_ziak':'1'})
2 >>> print form.is_valid()
3 False
4 >>> print form
5 <tr><th><label for="id_meno">Meno:</label></th><td><ul class="errorlist"><li>Ensure this value has at most 20 characters (it has 33).</li></ul><input id="id_meno" type="text" name="meno" value="Jozefxxxxxxxxxxxxxxxxxxxxxxxxxxxx" maxlength="20" /></td></tr>
6 <tr><th><label for="id_priezvisko">Priezvisko:</label></th><td><ul class="errorlist"><li>This field is required.</li></ul><input id="id_priezvisko" type="text" name="priezvisko" maxlength="20" /></td></tr>
7 <tr><th><label for="id_znamka">Znamka:</label></th><td><ul class="errorlist"><li>Ensure this value is less than or equal to 5.</li></ul><input type="text" name="znamka" value="10" id="id_znamka" /></td></tr>
8 <tr><th><label for="id_protekcny_ziak">Protekcny ziak:</label></th><td><input checked="checked" type="checkbox" name="protekcny_ziak" value="1" id="id_protekcny_ziak" /></td></tr>
| |
| |
| |