À noter que l'absence de documentation n'a pas permis à ce billet d'être exhaustif... Quelques fonctionnalités des newforms ne seront pas abordées.
Commencer
Il convient tout d'abord de les importer :
from django import newforms as forms
Commençons par créer un formulaire. C'est très simple, un formulaire est une classe, dérivée de django.newforms.Form, dans laquelle on peut définir les différents champs qui seront disponibles, exactement comme dans les modèles.
class FormulaireCommentaire(forms.Form):
nom = forms.CharField()
email = forms.EmailField()
message = forms.CharField()
Pour créer le formulaire, il suffit d'instancier la classe :
form = FormulaireCommentaire()
Méthodes et attributs
Une classe forms.Form peut prendre plusieurs arguments. Le premier permet de le pré-remplir (par exemple, s'il a déjà été envoyé mais qu'il contient des erreurs, et qu'il doit être réaffiché).
Vous pouvez également spécifier l'argument auto_id, qui est un motif pour les identifiants des champs : champ_%s_formulaire donnera, pour le "nom", champ_nom_formulaire.
data = request.POST.copy () form = FormulaireCommentaire(data, auto_id='commentaire_%s')
L'attribut is_bound renvoie True si le formulaire contient des données, False sinon. Quand vous y avez inséré du contenu, vous pouvez vérifier sa validité en testant form.is_valid(). Si certains champs sont mal remplis, form.errors retourne un dictionnaire de la forme {'champ': [u'Erreur']} ; mais vous pouvez également afficher une erreur particulière en utilisant form['champ'].errors.
Quand votre formulaire est valide - et seulement lorsqu'il l'est -, vous pouvez accéder à l'attribut clean_data, qui contient les données (sous la forme d'un dictionnaire) converties en objets Python correspondants. Pour savoir quel champ retourne quel type de données, regardez la liste des types de champs plus bas. De la même façon que pour les erreurs, vous pouvez accéder au contenu d'un champ en utilisant form.clean_data['champ']. Si le formulaire n'est pas valide, vous devrez vous baser sur les données POST : request.POST['champ'], mais ce ne sera pas un objet Python.
Avant d'aller plus loin, regardons les arguments communs à tous les champs :
required
C'est assez parlant, True (défaut) pour que le champ doive être rempli, False pour qu'il puisse rester vide.
widget
L'argument widget permet de choisir comment afficher un champ, plus d'explications dans la suite.
label
Permet de spécifier le nom du champ.
help_text
Permet de spécifier un texte qui apporte des précisions sur le champ.
initial
La valeur par défaut que prendra le champ.
Afficher les formulaires
Pour afficher le formulaire, vous pouvez simplement le transmettre à votre gabarit, puis l'afficher avec {{ form }}. Mais par défaut, il est rendu comme faisant partie d'un tableau, ce qui est loin de convenir à la majorité des cas. Vous pourrez ainsi faire {{ form.as_p }} pour afficher le formulaire dans des paragraphes, et {{ form.as_ul }} pour l'afficher sous forme de liste. Attention, si vous avez passé le paramètre auto_id à False, les <label> ne seront pas affichés ! De ces trois façons, les messages d'erreur seront également affichés sous les champs concernés.
Cependant, on peut désirer personnaliser un peu plus la sortie HTML du formulaire, et Django le permet. Ainsi, form['nom'] affichera seulement l'<input> ; form['nom'].errors les erreurs, et @@form['nom'].label l'intitulé du champ. Il faudra donc assembler ces éléments.
Widgets
L'argument widget permet d'associer un type de champ (sortie HTML) à l'un d'entre eux. Par exemple, un même champ classique CharField pourra être affiché avec différents widgets : TextInput (par défaut), PasswordInput, HiddenInput, Textarea... Ils permettent donc de choisir comment afficher un champ, en fonction de son objectif. Il est également possible de créer les vôtres en dérivant la classe forms.Widget (ou l'un d'entre eux directement si vous souhaitez ne modifier qu'un détail), si vous avez un besoin plus spécifique. Parmi les autres widgets disponibles, retenons FileInput, CheckboxInput, Select, SelectMultiple, RadioSelect, CheckboxSelectMultiple, dont les noms sont suffisamment parlants. Attention, les widgets n'influent pas sur le contenu du champ, seulement sur la manière dont il est présenté.
Exemple
Voici comment afficher un formulaire basique de commentaire, et l'ajouter à la base de données.
def vue_article(request, id_article):
class FormulaireCommentaire(forms.Form):
nom = forms.CharField("Votre nom")
email = forms.EmailField("Votre email", help_text = "Veuillez indiquer une adresse mail valide", required = False)
message = forms.CharField(widget = forms.Textarea)
try:
article = Article.objects.get(id=id_article)
except Article.DoesNoExist:
raise Http404
form = FormulaireCommentaire()
if request.method =='POST':
#On copie les données POST
data = request.POST.copy()
#Et on crée un formulaire avec
form = FormulaireCommentaire(data)
message = None
#S'il ne contient pas d'erreur...
if form.is_valid():
#On peut créer un nouveau commentaire
comment = Comment(nom = form.clean_data['nom'], email = form.clean_data['email'], message = form.clean_data['message']
#Et l'enregister
comment.save()
#On remplace form par un nouveau formulaire vide
form = FormulaireCommentaire()
message = "Commentaire enregistré avec succès !"
return render_to_response("article.html", {'article': article, 'form': form})
Ensuite, votre gabarit :
{% extends base %}
{% block content %}
article.text
{% endblock content %}
{% block content_bottom %}
{# Si on a un message de succès, on l'affiche. #}
{% if message %}
<p class="message"> message
</p>
{% endif %}
<form action="" method="POST">
{# On fait une boucle sur le formulaire pour avoir une sortie plus personnalisée, et car ce ne serait pas intéressant, sinon ! #}
{% for field in form %}
{# On n'oublie pas de relier le label au champ, grâce à l'attribut auto_id #}
<p><label for="{{ field.auto_id }}">{{ field.label }}</label>
{{ field }}
{% if field.errors %}
<span class="error">
{{ field.errors }}
</span>
{% endif %}
{# On affiche la ligne d'aide si jamais elle est présente #}
{% if field.help_text %}
<span class="help">
{{ field.help_text }}
</span>
{% endif %}
</p>
{% endfor %}
</form>
{% endblock content_bottom %}
Conclusion
Voici un bref aperçu des nouveaux formulaires de Django. Si ils permettent d'harmoniser leur création avec la structure des modèles en offrant une syntaxe cohérente et puissante, ils présentent le défaut majeur de ne pas pouvoir regrouper certains champs pour former des <fieldset>, qui se révèlent pourtant très utiles dans la création de formulaires avancés. Cela m'oblige à utiliser des palliatifs grossiers dans mon gabarit... et ça ne me plaît pas !
Les champs
Voici maintenant les différents types de champs disponibles dans une classe forms.Form :
Field
Classe de base, qui représente un champ de texte simple. clean retourne une chaîne.
CharField
Comme Field, mais un peu plus avancé, avec les arguments min_length et max_length qui stipulent respectivement un nombre minimum et maximum de caractères. clean retourne une chaîne.
IntegerField
Un entier ; prend les arguments min_value et max_value pour choisir les nombres auquels la valeur ne doit respectivement pas être inférieure et supérieure. clean retourne un entier.
DateField
Une date, prend un argument input_formats qui peut être un tuple de formats de date acceptés, comme par exemple "%d/%m/%Y" pour "7/3/2007". Par défaut, un bon nombre sont déjà définis... clean retourne un objet datetime.date.
TimeField
Une heure, prend un argument input_formats qui peut être un tuple de formats d'heure acceptés, comme par exemple "%Hh%M:%S" pour "17h23:12". clean retourne un objet datetime.time.
DateTimeField
Une date et une heure, prend un argument input_formats qui peut être un tuple de formats de date et heure acceptés, comme par exemple "%Hh%M:%S %d/%m/%Y" pour "17h23:12 7/3/2007". clean retourne un objet datetime.datetime.
RegexField
Comme CharField, à la différence que son contenu doit correspondre à l'expression régulière fournie à l'argument regex (requis). Prend également un argument error_message, qui sera retourné si l'expression régulière ne valide pas le champ. clean retourne une chaîne.
EmailField
Une adresse email valide, avec les arguments min_length et max_length. clean retourne une chaîne.
URLField
Une URL valide (du format http://www.aozeo.com/). Prends un argument verify_exists, qui, passé à True, vérifiera que l'URL répond et ne renvoie pas une 404 ; validator_user_agent permet de choisir l'USER-AGENT qui sera envoyé lors du test. clean retourne une chaîne.
BooleanField
Un champ vrai ou faux classique. clean retourne True ou False.
ChoiceField
Un choix entre les différentes options passées en argument à choices, sous la forme d'un tuple : ( ('identifiant_option_1', 'Option 1'), ('identifiant_option_2', 'Seconde option') ). clean retourne une chaîne.
MultipleChoiceField
Comme ChoiceField, mais autorise la sélection de plusieurs valeurs. clean retourne une liste de chaînes.


