Django : formulaire qui édite aussi des champs d’un modèle clé étrangère
Déclarer les modèles
Voici mes modèles en résumé, parce que vous y arriverez nécessairement un jour ou l’autre :
class Personne(models.Model):
user = models.OneToOneField(User)
date_naissance = models.DateField(default=None,
null=True, blank=True,
verbose_name=_(u'Birth date'))
class Meta(BaseModel.Meta):
ordering = ['date_v_debut']
Si on veut faire une forme qui édite nom
, prenom
et date_naissance
: facile… pour date_naissance
. Faire la forme avec les champs du modèle :
class ProfileForm(forms.ModelForm):
class Meta:
model = Personne
fields = ('date_naissance',)
Ajoutons la date de naissance :
a = _(u'Birthdate:')
date_naissance = forms.DateField(
label=a,
widget=forms.DateInput(attrs={'title': a}))
Déclarer les champs de la clé étrangère
Etape précédente facile ! Mais pour les champs d’un modèle clé étrangère, ici User
?
D’abord l’exclure de la forme (ne vous inquiétez pas, je ne vous mets qu’une partie du code, mais vous aurez le code en entier à la fin !) : exclude = ('user',)
Ensuite mettre les champs dans __init__
, ici exemple avec last_name
:
def __init__(self, *args, **kwargs):
super(ProfileForm, self).__init__(*args, **kwargs)
a = _(u'Last name:')
self.fields['user_last_name'] = forms.CharField(
label=a, max_length=100,
widget=forms.TextInput(attrs={
'title': a, 'size': 100, 'type': 'text',
'placeholder': _(u'my last name'),
'class': 'form-control'}),
error_messages=self.e)
Donc là vous aurez tous vos champs affichés, mais : (1) ils ne seront pas remplis avec la valeur de votre modèle si vous êtes en mode édition et (2) ils ne sont pas ordonnés, car ajoutés à la fin = ils apparaîtront forcément à la fin.
Pré-remplir le champ
Voici comment pré-remplir le champ : ce n’est pas dans la déclaration de la forme, mais dans la déclaration de la vue via get_initial()
:
class EditView(LoginRequiredMixin, generic.UpdateView):
model = Personne
template_name = 'my_home/profile/edit.html'
form_class = ProfileForm
success_url = reverse_lazy('my_home_index')
def get_initial(self):
initial = super(EditView, self).get_initial()
a = self.object.user
initial['user_last_name'] = a.last_name if a.last_name else u''
return initial
Les étapes en résumé
Ce titre = principal = h1.
There’s no need to say more… 🙂
Modèles
User
Déjà fait, merci Django ! Ahahaha. Bon.
Personne
class Personne(models.Model):
user = models.OneToOneField(User)
date_naissance = models.DateField(default=None,
null=True, blank=True,
verbose_name=_(u'Birth date'))
class Meta(BaseModel.Meta):
ordering = ['date_v_debut']
Forme
Champs normaux + champs de la clé étrangère = du modèle “étranger” :
class ProfileForm(forms.ModelForm):
class Meta:
model = Personne
fields = ('sexe', 'statut', 'est_fumeur',
'est_physique', 'date_naissance')
exclude = ('user', 'est_physique')
a = _(u'Birthdate:')
date_naissance = forms.DateField(
label=a,
widget=forms.DateInput(attrs={
'title': a,
'class': 'form-control datetimepicker'}))
def __init__(self, *args, **kwargs):
super(ProfileForm, self).__init__(*args, **kwargs)
a = _(u'Last name:')
self.fields['user_last_name'] = forms.CharField(
label=a, max_length=100,
widget=forms.TextInput(attrs={
'title': a, 'size': 100, 'type': 'text',
'placeholder': _(u'my last name'),
'class': 'form-control'}),
error_messages=self.e)
Vue
Pré-remplir les champs :
class EditView(LoginRequiredMixin, generic.UpdateView):
model = Personne
template_name = 'my_home/profile/edit.html'
form_class = ProfileForm
success_url = reverse_lazy('my_home_index')
def get_initial(self):
initial = super(EditView, self).get_initial()
a = self.object.user
initial['user_last_name'] = a.last_name if a.last_name else u''
return initial
Voilà résumé : Vue + Forme + Modèle. Inévitable sur des bons frameworks, et classique non ?
J’ai oublié une dernière chose : ces champs sont dans un OrderedDict()
c’est à dire que c’est l’ordre dans lequel ont été mis les éléments qui importe, donc dans notre cas, ils seront affichés en dernier. Si jamais vous voulez les afficher en premier, vous êtes obligés de reconstruire un OrderedDict()
dans lequel vous ajoutez vos champs dans l’ordre que vous désirez. Ici, je ne vais que remettre mon champ en premier, et remettre les autres.
Voici le code de __init__
final :
def __init__(self, *args, **kwargs):
super(ProfileForm, self).__init__(*args, **kwargs)
field_user_first_name = forms.CharField(
label=a, max_length=100,
widget=forms.TextInput(attrs={
'title': a, 'size': 100, 'type': 'text',
'placeholder': _(u'my first name'),
'class': 'form-control'}),
error_messages=self.e)
new_fields = OrderedDict([
('user_first_name', field_user_first_name),
])
for k, v in self.fields.items():
new_fields[k] = v
self.fields = new_fields