Django form.ChoiceField
| Date: | 2008-11-08 |
|---|---|
| Authors: | Stéphane KLEIN <stephane at harobed.org> |
| Status: | Publié |
L'objectif de ce document est d'expliquer comment utiliser form.ChoiceField, form.ModelChoiceField.
Contexte :
- Python
- Django 1.0
Objectif :
- présenter les champs formulaires de types ChoiceField et ModelChoiceField
Points non abordés :
- je n'aborde pas l'utilisation d'un objet formulaire dans une vue, pour cette partie, je vous laisse consulter la documentation Django (voir à la fin de ce document)
- création automatique d'un formulaire à partir d'un model
1 Code source de base
Fichier "projet1/app1/models.py" :
1 2 3 4 5 6 7 | from django import models
class Contact(models.Model):
nom = models.CharField(max_length=30)
prenom = models.CharField(max_length=30)
mail = models.EmailField()
groupe = models.CharField(max_length=30)
|
Fichier "projet1/app1/forms.py" :
1 2 3 4 5 6 7 8 9 10 | from django import forms
class ContactForm(forms.Form):
nom = forms.CharField( max_length = 30 )
prenom = forms.CharField( max_length = 30 )
mail = forms.EmailField()
groupe = forms.ChoiceField(
choices = ((u"GROUPE_A", u"Groupe A"), (u"GROUPE_B", u"Groupe B"),
(u"GROUPE_C", u"Groupe C"), (u"GROUPE_D", u"Groupe D"))
)
|
2 Principe général
+-------------------+
| forms.ContactForm | --\
| (mon formulaire) | | +-----------------+ +-------------------+
+-------------------+ +--| forms.CharField | --> | widgets.TextInput |
| | (nom) | +-------------------+
| +-----------------+
|
| +-----------------+ +-------------------+
+--| forms.CharField | --> | widgets.TextInput |
| | (prenom) | +-------------------+
| +-----------------+
|
| +-----------------+ +-------------------+
+--| forms.EmailField| --> | widgets.TextInput |
| | (mail) | +-------------------+
| +-----------------+
|
| +------------------+ +---------------------+
+--| forms.ChoiceField| --> | widgets.SelectInput |
| (groupe) | +---------------------+
+------------------+
- la classe "forms.Form" représente un formulaire
- un formulaire contient un ou plusieurs champs
- il existe plusieurs types de champ : BooleanField, CharField, ChoiceField, DateField, DecimalField...
- chaque type de champ est défini par une classe
- chaque type de champ est lié à un widget afin de pouvoir être affiché
2.1 Les propriétés des classes forms.*Field
Les classes qui définissent les champs d'un formulaire (forms.*Field) ont des propriétés communes entre elles ainsi que des propriétés spécifiques.
Exemples de propriétés communes :
- "required" : pour définir un champ obligatoire
- "label" : le nom du champ affiché dans l'interface utilisateur
- "initial" : valeur initiale du champ (rien à voir avec les valeurs par défaut au niveau de la couche modèle)
- "widget" : le widget qui sera utilisé pour afficher le champ
- "help_text" : le message d'aide du champ qui sera affiché au niveau de l'interface utilisateur
- ...
Exemples de propriétés spécifiques :
- forms.CharField : "max_length" et "min_length"
- forms.ChoiceField : "choices"
- forms.DateField : "input_formats"
- forms.DecimalField : "max_value", "min_value", "max_digits", "decimal_places"
- ...
La plupart des propriétés ont des valeurs par défaut, exemple :
par défaut la propriété "required" est désactivé
- la propriété "widget" a pour valeur par défauts :
- "CharField" => widget TextInput
- "ChoiceField" => widget Select
- "DateField" => TextInput
- ...
Où trouver ces informations ? Form fields : liste des champs formulaires disponibles dans Django.
3 Champ de formulaire de type "forms.ChoiceField"
Fichier "projet1/app1/forms.py" :
...
class ContactForm(forms.Form):
...
groupe = forms.ChoiceField(
choices = ((u"GROUPE_A", u"Groupe A"), (u"GROUPE_B", u"Groupe B"),
(u"GROUPE_C", u"Groupe C"), (u"GROUPE_D", u"Groupe D"))
)
Un champ de formulaire de type ChoiceField permet de sélectionner une valeur à attribuer à un champ de la couche modèle en fonction d'une liste de choix.
Dans l'exemple ci-dessus, le champ de formulaire "groupe" attribuera la valeur u"GROUPE_A", u"GROUPE_B", u"GROUPE_C" ou u"GROUPE_D" au champ "groupe" de l'objet de type "models.Contact".
3.1 Propriété "choices" de la classe "forms.ChoiceField"
La liste de choix est définie par la propriété "choices" de la classe "forms.ChoiceField". La valeur de cette propriété doit être un tuple ou une liste. Ce tuple ou cette liste doivent contenir un autre tuple ou liste de deux éléments :
- la valeur de l'élément
- le label de l'élément
choices = ((u"VALEUR1", u"Label1"), (u"VALEUR2", u"Label2"), ... )
ou alors
choices = [(u"VALEUR1", u"Label1"), (u"VALEUR2", u"Label2"), ... ]
En savoir plus sur les tuples...
En savoir plus sur les listes...
3.2 Propriété "widget" de la classe "forms.ChoiceField"
La propriété "widget" de la classe "forms.ChoiceField" a pour valeur par défaut "SelectInput". Cela signifie que par défaut, un champ ChoiceField utilisera une balise HTML de type "<select><option ... </select>' au niveau de l'interface utilisateur.
Maintenant, si à la place d'une balise "select" nous souhaitons utiliser des balises "<input type='radio' ... />" nous devons affecter une autre valeur à la propriété "widget".
Exemple :
...
class ContactForm(forms.Form):
...
groupe = forms.ChoiceField(
widget = forms.RadioSelect(),
choices = ((u"GROUPE_A", u"Groupe A"), (u"GROUPE_B", u"Groupe B"),
(u"GROUPE_C", u"Groupe C"), (u"GROUPE_D", u"Groupe D"))
)
Pour chaque champ d'un formulaire, nous avons la liberté d'utiliser librement le widget qui correspond le mieux à nos désirs à condition de respecter la compatibilité champ / widget. De plus, nous avons aussi la possibilité de créer de nouveaux widgets qui seront par la suite réutilisables.
4 Champ de formulaire de type "form.ModelChoiceField"
Voici le code source de notre précédent exemple avec quelques modifications.
4.1 Code source modifié
Fichier "projet1/app1/models.py" :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | from django import models
class Contact(models.Model):
nom = models.CharField(max_length=30)
prenom = models.CharField(max_length=30)
mail = models.EmailField()
groupe = models.ForeignKey("Groupe")
class Groupe(models.Model):
label = models.CharField(max_length = 20)
def __unicode__(self):
return self.label
|
Fichier "projet1/app1/forms.py" :
1 2 3 4 5 6 7 8 9 10 11 12 13 | from django import forms
from projet1.app1.models import Groupe
class ContactForm(forms.Form):
nom = forms.CharField( max_length = 30 )
prenom = forms.CharField( max_length = 30 )
mail = forms.EmailField()
groupe = forms.ModelChoiceField(
queryset = Groupe.objects.all(),
widget = forms.RadioSelect(),
empty_label = None
)
|
4.2 Qu'est ce qui a été modifié ?
- Le groupe auquel appartient un Contact n'est plus un champ de type texte (CharField) mais une relation avec une entité "Groupe"
- Une nouvelle entité a été ajouté : Groupe
- Au niveau du formulaire contact, le champ Groupe a été modifié afin d'utiliser automatiquement une liste d'entité "Groupe"
4.3 Détail du champ "forms.ModelChoiceField"
La classe "ModelChoiceField" a plusieurs propriétés qui lui sont propres :
- "queryset" : défini la requête qui sera utiliser comme liste de choix, ici la liste complète des entités "Groupe"
- "empty_label" : défini la valeur par défaut du choix, ici aucun
"ModelChoiceField" diffère de "ChoiceField" parce que ce dernier utilise un tuple ou une liste comme liste de choix alors que "ModelChoiceField" utilise une liste dynamique. A noter qu'il est tout à fait possible de générer dynamiquement un tuple ou une liste qui sera ensuite attribué à la propriété "choice" d'un objet de type "ChoiceField".
Note : au moment où j'écris ces lignes, la propriété "empty_label" n'est pas documenté dans la documentation Django. Je viens de signaler cette absence par un ticket dont voici le lien "http://code.djangoproject.com/ticket/9533". À noter qu'il est possible de consulter une documentation de l'API généré automatiquement... par exemple grâce à "pydoc"... dans cette documentation la propriété "empty_label" est visible...
4.4 Attention à ne pas faire la même erreur que moi
La première fois que j'ai testé la classe "forms.ModelChoiceField" je n'avais pas modifié le type "groupe" au niveau de la couche modèle. J'avais gardé un champ de type "models.CharField". Là était mon erreur.
Je n'avais aucun problème au niveau de la génération de la liste de choix. Le widget s'affichait parfaitement lors de l'affichage du formulaire dans mon navigateur.
Par contre j'avais une erreur lors de l'enregistrement des données. La classe "forms.ModelChoiceField" essayait d'affecter un objet de type "Groupe" au champ "groupe" de ma classe "Contact". Or ce champ ne pouvait pas accepter un objet, normal il était de type "models.CharField".
Pourquoi ? parce que ModelChoiceField doit être utilisé sur une relation. Sinon il faut utiliser un champ formulaire de type "forms.ChoiceField".
5 La documentation
- Working with forms : introduction au mécanisme des gestions des formulaires Django;
- The Forms API : détail de l'API de la classe formulaire (un objet formulaire contient un ou plusieurs champs)
- Form fields : liste des champs formulaires disponibles dans Django;
- Widgets : liste des widgets;
6 Code source d'exemple
Vous trouverez à l'adresse suivante http://www.harobed.org/download/django_choicefield_examples.tar.gz, le code source de plusieurs exemples qui mettent en oeuvre les différents exemples d'utilisation de ChoiceField vu précédemment.
Les différents exemples :
- exemple1 : mise en oeuvre simple de ChoiceField
- exemple2 : mise en oeuvre de ChoiceField avec le widget RadioSelect
- exemple3 : mise en oeuvre de ModelChoiceField
Utilisation : si Django version 1.0 est installé sur votre système, il vous suffira d'exécuter "python manager.py runserver" dans le dossier "projet1" de l'exemple que vous souhaitez tester.
Url à tester :
Accès à la zone d'administration :
- http://localhost:8000/admin/
- identifiant : harobed
- mot de passe : password
Attention :
- le code source de ces exemples n'est pas parfait
- il y a plus simple et court : l'utilisation des vues génériques
- utilisation d'"include" dans le fichier urls.py
- ...
7 Dans l'interface d'administration de Django
L'interface d'administration de Django vous laisse la possibilité de modifier l'aspect des listes de choix.
Voici un exemple :
1 2 | class PersonAdmin(admin.ModelAdmin):
radio_fields = {"group": admin.VERTICAL}
|
Voici le lien vers la documentation en question : http://docs.djangoproject.com/en/dev/ref/contrib/admin/#radio-fields