Tutos Django

Les formulaires en Django

La création de formulaires avec Django est extrêmement simple et bourrée de fonctionnalités.

Vous pouvez générer des formulaires à partir de vos modèles ou bien les créer directement depuis des classes.

Passons tout de suite à la pratique, et mettons cela en place. Pour plus de propreté, nous allons créer une nouvelle application, que nous appellerons 'myform'.

$ python manage.py startapp myform

Et on n'oublie pas de renseigner notre application dans le fichier settings.py de notre projet :

INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    # Uncomment the next line to enable the admin:                                                                                                                                                                   
    # 'django.contrib.admin',                                                                                                                                                                                        
    # Uncomment the next line to enable admin documentation:                                                                                                                                                         
    # 'django.contrib.admindocs',                                                                                                                                                                                    
    'helloworld',
    'myform',
)

Vous devriez comprendre comment cela fonctionne maintenant :)

On va à présent créer le modèle de notre application, on ouvre pour cela le fichier models.py contenu dans le dossier de notre application nommée 'myform' et on l'édite comme ceci :

from django.db import models

# Create your models here.                                                                                                                                                                                           
class Contact(models.Model):
    name = models.CharField(max_length=200)
    firstname = models.CharField(max_length=200)
    email = models.EmailField(max_length=200)
    message = models.CharField(max_length=1000)

On crée donc une classe 'Contact' qui contiendra un nom, un prénom, un email et un message.

Générons la en base de données :

$ python manage.py syncdb
Creating tables ...
Creating table myform_contact
Installing custom SQL ...
Installing indexes ...
No fixtures found.

Et voilà, notre modèle est présent en base de données, maintenant passons à la création de notre contrôleur afin de générer notre formulaire. On ouvre pour cela le fichier views.py présent dans notre application, et on l'édite de telle façon :

from django.shortcuts import render_to_response
from django.forms import ModelForm
from models import Contact


class ContactForm(ModelForm):
    class Meta:
        model = Contact


def contact(request):

    contact_form = ContactForm()
    return render_to_response('contact.html', {'contact_form' : contact_form})

On importe notre modèle ainsi que notre 'helper' render_to_response, ainsi qu'une nouveauté 'ModelForm'.
On définit alors notre classe qui va permettre de générer notre formulaire en la faisant dériver de ModelForm et en lui spécifiant le modèle à inclure "Contact".
Il ne nous reste plus qu'à déclarer une nouvelle variable qui sera une instance de la nouvelle classe créée et de la passer en tant que donnée à notre template.

Maintenant, il va falloir définir à quel template est associé ce contrôleur, on va donc modifier le fichier urls.py de notre projet en ajoutant les lignes :

urlpatterns += patterns('mysite.myform.views',
                       url(r'^contact$', 'contact', name='contact'),
)

Vous remarquerez ici que j'ajoute des données à la variable 'urlpatterns' en utilisant l'opérateur '+=', de plus je définis le premier argument à "mysite.myform.views", ce qui me permet de définir le chemin du fichier où sont stockés les fonctions de mon contrôleur (ici la fonction contact).

Il faut également que le dossier 'templates' de notre nouvelle application soit référencé dans les paramètres du projet, ouvrez pour cela le fichier settings.py de votre projet et éditez-le de telle façon :

TEMPLATE_DIRS = (
    # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".                                                                                                                             
    # Always use forward slashes, even on Windows.                                                                                                                                                                   
    # Don't forget to use absolute paths, not relative paths.                                                                                                                                                        
    '/path_to/mysite/helloworld/templates',
    '/path_to/mysite/myform/templates',
)

Créons maintenant notre template ! On commence par créer un dossier 'templates' dans notre application :

$ cd myform
$ mkdir templates
$ cd templates

et on crée un fichier contact.html que l'on remplit comme ceci :

{{ contact_form.as_p  }}

Voilà, pas plus ! Nous affichons ici directement notre objet contact_form et on lui associe une option d'affichage 'as_p' qui signifie que le formulaire sera affiché en utilisant la balise "<p>"

Nous pouvons à présent apprécier le résultat ! lancez votre serveur :

$ python manage.py runserver

et dirigez-vous à l'adresse : http://127.0.0.1:8000/contact vous devriez obtenir ceci :

Form simple

Notre formulaire html utilisant des balises '<p>' a été automatiquement généré ! Pour l'instant il reste encore assez simple, mais nous allons voir que nous allons pouvoir très vite tout personnaliser et avoir un bien meilleur rendu ! D'autres options d'affichages telles que 'as_ul' et 'as_table' aurait pu être utilisées, pour plus d'informations sur l'affichage des forms, je vous conseille de lire cette partie de la documentation officielle : Outputting forms as HTML

Mais avant cela, nous allons nous intéresser à la création de formulaire sans utiliser de modèles ! Directement avec le module forms de Django ! Pour ceux qui souhaitent approfondir la création de formulaire à partir de modèles, voici la documentation officielle : Creating forms from models

Créons maintenant notre formulaire sans utiliser de modèle, ouvrez votre fichier views.py de l'application 'myform' et éditez le de cette façon :

from django.shortcuts import render_to_response
from django.forms import ModelForm
from models import Contact


class ContactForm(ModelForm):
    class Meta:
        model = Contact

from django import forms

class ContactForm2(forms.Form):
    name = forms.CharField(max_length=200)
    firstname = forms.CharField(max_length=200)
    email = forms.EmailField(max_length=200)
    message = forms.CharField(max_length=1000)

def contact(request):

    contact_form = ContactForm()
    contact_form2 = ContactForm2()
    return render_to_response('contact.html', {'contact_form' : contact_form, 'contact_form2' : contact_form2})

On inclut directement le module 'forms' de django et on crée une classe 'ContactForm2' dérivant de Form et non de ModelForm cette fois-ci.
On crée ainsi notre classe, ce qui ressemble sensiblement à la déclaration de notre modèle.
Puis on crée un objet contact_form2 dans notre fonction et on le passe en donnée de notre template.

Maintenant éditez votre fichier contact.html de cette façon :

{{ contact_form.as_p }}
<br />
{{ contact_form2.as_p }}

On inclut ici nos deux formulaires et on obtient comme résultat après redémarrage du serveur :

form_model_and_forms

La même chose ! On obtient donc deux formulaires identiques créés de deux façons différentes !
Voici la liste des correspondances des déclarations d'un 'Model' vers un 'Form' grâce à 'ModelForm' : Field types

Intéressons-nous maintenant à la personnalisation de ces formulaires avant de comprendre comment les utiliser et leur validation.

Les formulaires de type ModelForm ainsi que ceux de type Form peuvent contenir de nombreuses options de personnalisation.
Voici la documentation officielle concernant les champs qui peuvent être utilisés pour 'Form' : Form fields
On peut tout de suite remarquer certaines options telles que : required, label, initial, widget, help_text, error_messages, validator, localize

- 'required' va spécifier si le champs du formulaire peut être ignoré ou non, par défaut tous les champs sont requis. Cela implique que si l'utilisateur valide le formulaire avec des champs à vide ("" ou None), une erreur sera levée. Pour illustrer cela, voici ce que donne les tests suivant après avoir ouvert une console python en mode django et avoir importé le module forms :

>>> f = forms.CharField()
>>> f.clean('foo')
u'foo'
>>> f.clean('')
Traceback (most recent call last):
...
ValidationError: [u'This field is required.']
>>> f = forms.CharField(required=False)
>>> f.clean('foo')
u'foo'
>>> f.clean('')
u''
>>> f.clean(None)
u''

Nous simulons ici la vérification de formulaire avec la méthode 'clean'. Comme vous pouvez le constater, une erreur est bien levée si nous passons une chaîne vide '' ou None à notre champs.

- 'label' va permettre de réécrire la façon dont le label du champs est affiché, par défaut, cela prend le nom de la variable et lui ajoute une majuscule à la première lettre et remplace tous les underscores par des espaces. Exemple :

>>> class CommentForm(forms.Form):
...     name = forms.CharField(label='Your name')
...     url = forms.URLField(label='Your Web site', required=False)
...     comment = forms.CharField()
... 
>>> f = CommentForm()
>>> print f
<tr><th><label for="id_name">Your name:</label></th><td><input type="text" name="name" id="id_name" /></td></tr>
<tr><th><label for="id_url">Your Web site:</label></th><td><input type="text" name="url" id="id_url" /></td></tr>
<tr><th><label for="id_comment">Comment:</label></th><td><input type="text" name="comment" id="id_comment" /></td></tr>

Vous voyez également qu'ici on affiche notre 'form' sans utiliser d'option d'affichage, il est donc affiché par défaut en utilisant une table.
Pour plus de commodité, on aurait pu rajouter l'option 'auto_id' à 'False', ce qui aurait pour effet de désactiver la création d'une balise label, et génèrerait ceci :

>>> f = CommentForm(auto_id=False)
>>> print f
<tr><th>Your name:</th><td><input type="text" name="name" /></td></tr>
<tr><th>Your Web site:</th><td><input type="text" name="url" /></td></tr>
<tr><th>Comment:</th><td><input type="text" name="comment" /></td></tr>

- 'initial' va permettre de mettre des valeurs par défaut à vos champs, exemple :

>>> class CommentForm(forms.Form):
...     name = forms.CharField(initial='Your name')
...     url = forms.URLField(initial='http://')
...     comment = forms.CharField()
>>> f = CommentForm(auto_id=False)
>>> print f
<tr><th>Name:</th><td><input type="text" name="name" value="Your name" /></td></tr>
<tr><th>Url:</th><td><input type="text" name="url" value="http://" /></td></tr>
<tr><th>Comment:</th><td><input type="text" name="comment" /></td></tr>

Nous nous arrêterons à ces trois options pour le moment et verrons les autres dans des cas plus poussés.

Par contre il est très intéressant de se pencher sur l'option 'widget' car elle va vous permettre de réaliser très facilement des champs spécifiques. Voici la documentation officielle : Widgets
On va pouvoir spécifier quel est le type de génération de notre champs et lui ajouter des attributs :

from django import forms

class CommentForm(forms.Form):
    name = forms.CharField()
    url = forms.URLField()
    comment = forms.CharField(widget=forms.Textarea)

Ou bien :

from django.forms.fields import DateField, ChoiceField, MultipleChoiceField
from django.forms.widgets import RadioSelect, CheckboxSelectMultiple
from django.forms.extras.widgets import SelectDateWidget

BIRTH_YEAR_CHOICES = ('1980', '1981', '1982')
GENDER_CHOICES = (('m', 'Male'), ('f', 'Female'))
FAVORITE_COLORS_CHOICES = (('blue', 'Blue'),
                            ('green', 'Green'),
                            ('black', 'Black'))

class SimpleForm(forms.Form):
    birth_year = DateField(widget=SelectDateWidget(years=BIRTH_YEAR_CHOICES))
    gender = ChoiceField(widget=RadioSelect, choices=GENDER_CHOICES)
    favorite_colors = forms.MultipleChoiceField(required=False,
        widget=CheckboxSelectMultiple, choices=FAVORITE_COLORS_CHOICES)

Les essayer en mode de rendu est intéressant ! Faites-le !
Un dernier pour la route :

class CommentForm(forms.Form):
    name = forms.CharField(
                widget=forms.TextInput(attrs={'class':'special'}))
    url = forms.URLField()
    comment = forms.CharField(
               widget=forms.TextInput(attrs={'size':'40'}))

Ce qui donnera :

>>> f = CommentForm(auto_id=False)
>>> f.as_table()
<tr><th>Name:</th><td><input type="text" name="name" class="special"/></td></tr>
<tr><th>Url:</th><td><input type="text" name="url"/></td></tr>
<tr><th>Comment:</th><td><input type="text" name="comment" size="40"/></td></tr>

N'hésitez pas à parcourir les différents types de widgets et à les essayer pour découvrir ce qu'ils produisent. Le passage à la documentation anglaise ne devrait pas être trop difficile ici :)
A noter que vous pouvez également utiliser les widgets avec des Form venant de Modèles : Overriding the default field types or widget
Example :

from django.forms import ModelForm, Textarea

class AuthorForm(ModelForm):
    class Meta:
        model = Author
        fields = ('name', 'title', 'birth_date')
        widgets = {
            'name': Textarea(attrs={'cols': 80, 'rows': 20}),
        }

Enfin, nous allons nous intéresser à la validation des formulaires et comment travailler avec ceux-ci. En référence voici la documentation officielle dont nous allons traiter une petit partie : Working with forms

Nous allons reprendre notre fichier views.py de notre application 'myform' et remplacer son contenu par celui-ci :

from django.shortcuts import render_to_response, HttpResponseRedirect, HttpResponse
from django import forms

class ContactForm(forms.Form):
    subject = forms.CharField(max_length=100)
    message = forms.CharField()
    sender = forms.EmailField()
    cc_myself = forms.BooleanField(required=False)

def contact(request):
    if request.method == 'POST': # If the form has been submitted...                                                                                                                                                 
        form = ContactForm(request.POST) # A form bound to the POST data                                                                                                                                             
        if form.is_valid(): # All validation rules pass                                                                                                                                                              
            subject = form.cleaned_data['subject']
            message = form.cleaned_data['message']
            sender = form.cleaned_data['sender']
            cc_myself = form.cleaned_data['cc_myself']

            # do_something                                                                                                                                                                                           

            return HttpResponseRedirect('/success') # Redirect after POST                                                                                                                                            
    else:
        form = ContactForm() # An unbound form                                                                                                                                                                       

    return render_to_response('contact.html', { 'form': form, })

def success(request):
    return HttpResponse('<p>Success</p>')

Et nous allons définir une nouvelle url pour la page "Success", donc ouvrez le fichier urls.py de votre projet et éditez-le de cette façon :

urlpatterns += patterns('mysite.myform.views',
                       url(r'^contact$', 'contact', name='contact'),
                        url(r'^success$', 'success', name='success'),
)

Nous avons donc défini un formulaire à partir du module 'forms' de Django en utilisant la classe 'Form'.
Puis nous créons la logique du code de la validation du formulaire.
Partons du bas de la fonction :

    else:
        form = ContactForm() # An unbound form                                                                                                                                                                       

    return render_to_response('contact.html', { 'form': form, })

Voilà ce qui va se passer la première fois que nous allons charger la page. Le formulaire est créé, et ensuite nous le renvoyons au template de rendu.
Une fois qu'il a été soumis, l'attribut 'method' de la variable 'request' va passer à 'POST' dans notre cas (cela pourrait être par exemple 'GET'). C'est alors que nous pouvons vérifier si le formulaire est valide, c'est à dire qu'il ne comporte pas d'erreurs, et une fois qu'il est valide nous pouvons récupérer les valeurs envoyées afin de les récupérer correctement formatées à l'aide l'attribut 'cleaned_data'.
Souvenez-vous précédemment, nous avions utilisé la méthode 'clean' pour simuler l'envoi de nos champs de formulaires et vérifier s'ils étaient correctement remplis. La méthode 'is_valid' agit un peu de la même sorte mais va elle au lieu de lever des exceptions, sauvegarder les erreurs dans l'attribut 'errors' de l'objet 'form' sous forme d'un dictionnaire.
Si tout se passe bien, nous renvoyons donc nos utilisateurs vers une page de succès.

Voici maintenant le code modifié de la page contact.html, éditez-le de cette façon :

<form action="/contact" method="post">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit" />
</form>

Testons à présent notre code, lancez votre serveur et cliquez sur le bouton "Submit", vous devriez obtenir ceci :

Form Forbidden

Cela est du au fait que nous avons rajouter ce bout de code dans notre template :

{% csrf_token %}

En effet, ceci indique que nous voulons protéger notre formulaire contre les attaques de type "Cross Site Request Forgery", vous pourrez trouvez plus d'informations à propos de cela sur la documentation officielle : Cross Site Request Forgery protection
Nous ne parlerons pas de cette protection dans ce tutoriel, mais voici comment à présent corriger l'erreur que nous avons obtenu.
En réalité, cette erreur vient du fait que nous avons oublié de passer le contexte de notre redirection, il va falloir donc explicitement le faire.

Pour cela remplacez :

return render_to_response('contact.html', { 'form': form, })

par :

from django.template import RequestContext
return render_to_response('contact.html', { 'form': form, }, context_instance=RequestContext(request))

Il existe plusieurs façons de faire ce retour, nous les verrons dans un prochain tutorial destiné aux raccourcis urls de django.

Lancez, à présent votre serveur et cliquez sur "Submit", vous devriez obtenir ceci :

Form Errors

Magique ! Les erreurs s'affichent toutes seules sans que nous ayons eu à spécifier quoique ce soit. Car souvenez-vous du code que nous avons créé, si le formulaire n'est pas valide nous renvoyons la même page mais cette fois-ci avec un formulaire contenant les erreurs rencontrées.

Remplissez le formulaire comme ceci à présent :

Form Filled

Appuyez sur le bouton "Submit" et observez le résultat :

Email Error

Vous observez qu'une erreur différente a été affichée !
Rentrez à présent une adresse email valide, et cliquez à nouveau sur "Submit", vous obtenez ceci :

Success

Notre page de succès a bien été affichée ! Vous auriez pu fournir des paramètres à cette page de succès comme les données soumises au formulaire afin de vérifier ce qui a été passé.

Vous remarquez que les erreurs qui ont été affichées automatiquement peuvent être complètement personnalisées là aussi, nous verrons ça dans un autre tutorial, en attendant, si vous voulez approfondir votre travail avec les form en Django, je vous conseille cette partie de la documentation officielle : Working with forms

Article suivant

Article précédent

Articles associés

Articles similaires

Commentaires

Les commentaires sont fermés.

Pingbacks

Les pingbacks sont fermés.