OUJOOD.COM
Rappel — ce que ModelForm fait automatiquement
Quand vous déclarez un ModelForm, Django inspecte la classe Meta.model et génère un champ de formulaire pour chaque colonne du modèle. Un CharField du modèle devient un CharField dans le formulaire, un IntegerField un IntegerField, une ForeignKey un ModelChoiceField avec une liste déroulante. Les contraintes du modèle (max_length, blank, null) sont reprises automatiquement.
Personnaliser la classe Meta
La classe Meta contrôle tous les aspects du formulaire généré :
from django import forms
from .models import Membres
class MembreForm(forms.ModelForm):
class Meta:
model = Membres
fields = ['prenom', 'nom', 'mail']
# Labels personnalisés
labels = {
'prenom': 'Prénom',
'nom': 'Nom de famille',
'mail': 'Adresse email',
}
# Textes d'aide affichés sous les champs
help_texts = {
'mail': 'Utilisé pour les notifications — jamais partagé.',
}
# Messages d'erreur personnalisés par champ et par type d'erreur
error_messages = {
'prenom': {
'required': 'Le prénom est obligatoire.',
'max_length': 'Le prénom ne peut pas dépasser 255 caractères.',
},
'mail': {
'required': 'L\'adresse email est obligatoire.',
'invalid': 'Entrez une adresse email valide.',
},
}
Personnaliser les widgets
Un widget contrôle comment un champ est rendu en HTML. Django choisit un widget par défaut selon le type de champ — TextInput pour un CharField, Select pour une ForeignKey. Vous pouvez le remplacer et y ajouter des attributs HTML.
class MembreForm(forms.ModelForm):
class Meta:
model = Membres
fields = ['prenom', 'nom', 'mail', 'bio']
widgets = {
# Ajouter des attributs HTML (class CSS, placeholder...)
'prenom': forms.TextInput(attrs={
'class': 'form-control',
'placeholder': 'Votre prénom',
}),
'nom': forms.TextInput(attrs={
'class': 'form-control',
}),
'mail': forms.EmailInput(attrs={
'class': 'form-control',
}),
# Remplacer un CharField par un Textarea
'bio': forms.Textarea(attrs={
'class': 'form-control',
'rows': 4,
}),
}
Widgets courants
TextInput — champ texte simple (<input type="text">).
Textarea — zone de texte multi-lignes.
PasswordInput — champ mot de passe (masque la saisie).
EmailInput — champ email avec validation HTML5.
NumberInput — champ numérique.
CheckboxInput — case à cocher.
Select — liste déroulante (utilisé automatiquement pour les ForeignKey).
DateInput — champ date avec attribut type="date".
Ajouter de la validation personnalisée
ModelForm accepte les mêmes méthodes de validation que Form : clean_nomchamp() pour un champ, clean() pour la validation croisée.
class MembreForm(forms.ModelForm):
class Meta:
model = Membres
fields = ['prenom', 'nom', 'mail']
def clean_mail(self):
mail = self.cleaned_data['mail']
# Pour un formulaire de modification, exclure l'objet courant
qs = Membres.objects.filter(mail=mail)
if self.instance.pk:
qs = qs.exclude(pk=self.instance.pk)
if qs.exists():
raise forms.ValidationError("Cette adresse email est déjà utilisée.")
return mail
save(commit=False) — modifier l'objet avant enregistrement
save() crée ou met à jour l'enregistrement directement. save(commit=False) retourne un objet Python non encore enregistré en base — vous pouvez le modifier avant de l'enregistrer manuellement. Très utile pour ajouter des champs qui ne figurent pas dans le formulaire.
def ajouter_membre(request):
if request.method == 'POST':
form = MembreForm(request.POST)
if form.is_valid():
# Crée l'objet Python sans l'enregistrer
membre = form.save(commit=False)
# Ajoute des données non présentes dans le formulaire
membre.ajoute_par = request.user
membre.actif = True
# Enregistrement en base
membre.save()
return redirect('index')
else:
form = MembreForm()
return render(request, 'ajout.html', {'form': form})
Gérer les relations ForeignKey dans un ModelForm
Quand un modèle a une ForeignKey, Django génère automatiquement un ModelChoiceField — une liste déroulante avec tous les objets liés. Vous pouvez filtrer cette liste en surchargeant __init__ :
from .models import Article, Categorie
class ArticleForm(forms.ModelForm):
class Meta:
model = Article
fields = ['titre', 'contenu', 'categorie']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Limiter les catégories affichées aux catégories actives
self.fields['categorie'].queryset = Categorie.objects.filter(active=True)
# Personnaliser le label de l'option vide
self.fields['categorie'].empty_label = "— Choisir une catégorie —"
Par carabde | Mis à jour le 06 mai 2026