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:

  1. 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.
  2. Programátor predpokladá, reťazce sú nanajvýš tak dlhé, ako vyžadoval vo formulári. Vedie k preťaženiu servra.
  3. Programátor predpokladá, že zadané reťazce neobsahujú žiadne špeciálne znaky. Toto vedie k dvom typom chýb:
    1. 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í.

    2. 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.

   1 >>> print form.is_valid()
   2 True

Vo form.cleaned_data máme teraz k dispozícii slovník s dátami.

   1 >>> print form.cleaned_data
   2 {'znamka': 3, 'meno': u'Jozef', 'priezvisko': u'Mak', 'protekcny_ziak': True}

Všimnite si, že dáta už sú správneho typu:

   1 >>> for key in form.cleaned_data:
   2 ...     print "typ hodnoty",key,"is",type(form.cleaned_data[key]).__name__
   3 ...
   4 typ hodnoty znamka is int
   5 typ hodnoty meno is unicode
   6 typ hodnoty priezvisko is unicode
   7 typ hodnoty protekcny_ziak is bool

Teraz vyskúšame, čo urobia chybné dáta. Ako znamka pošleme nečíslo.

   1 >>> form=MyForm({'meno':'Jozef','priezvisko':'Mak','znamka':'xxx','protekcny_ziak':'1'})
   2 >>> print form.is_valid()
   3 False

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:

  • Enter a whole number.

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>

  • Ensure this value has at most 20 characters (it has 33).
  • This field is required.
  • Ensure this value is less than or equal to 5.

KMaDGWiki: ProgramovanieInternetovychAplikacii/Forms (last edited 2009-03-18 08:22:28 by 95)