Est-il possible d'avoir plusieurs modèles inclus dans un seul ModelForm
dans django? J'essaie de créer un formulaire de modification de profil. Je dois donc inclure certains champs du modèle utilisateur et le modèle UserProfile. Actuellement j'utilise 2 formulaires comme celui-ci
class UserEditForm(ModelForm):
class Meta:
model = User
fields = ("first_name", "last_name")
class UserProfileForm(ModelForm):
class Meta:
model = UserProfile
fields = ("middle_name", "home_phone", "work_phone", "cell_phone")
Existe-t-il un moyen de les consolider en un seul formulaire ou dois-je simplement créer un formulaire et gérer le chargement et la sauvegarde de la base de données?
Vous pouvez simplement afficher les deux formulaires dans le modèle à l'intérieur d'un élément html <form>
. Ensuite, traitez simplement les formulaires séparément dans la vue. Vous pourrez toujours utiliser form.save()
et ne pas avoir à traiter le chargement et la sauvegarde de db.
Dans ce cas, vous ne devriez pas en avoir besoin, mais si vous allez utiliser des formulaires avec les mêmes noms de champ, consultez le prefix
kwarg pour Django formulaires. ( J'ai répondu à une question à ce sujet ici ).
Vous pouvez essayer d'utiliser ces morceaux de code:
class CombinedFormBase(forms.Form):
form_classes = []
def __init__(self, *args, **kwargs):
super(CombinedFormBase, self).__init__(*args, **kwargs)
for f in self.form_classes:
name = f.__name__.lower()
setattr(self, name, f(*args, **kwargs))
form = getattr(self, name)
self.fields.update(form.fields)
self.initial.update(form.initial)
def is_valid(self):
isValid = True
for f in self.form_classes:
name = f.__name__.lower()
form = getattr(self, name)
if not form.is_valid():
isValid = False
# is_valid will trigger clean method
# so it should be called after all other forms is_valid are called
# otherwise clean_data will be empty
if not super(CombinedFormBase, self).is_valid() :
isValid = False
for f in self.form_classes:
name = f.__name__.lower()
form = getattr(self, name)
self.errors.update(form.errors)
return isValid
def clean(self):
cleaned_data = super(CombinedFormBase, self).clean()
for f in self.form_classes:
name = f.__name__.lower()
form = getattr(self, name)
cleaned_data.update(form.cleaned_data)
return cleaned_data
Exemple d'utilisation:
class ConsumerRegistrationForm(CombinedFormBase):
form_classes = [RegistrationForm, ConsumerProfileForm]
class RegisterView(FormView):
template_name = "register.html"
form_class = ConsumerRegistrationForm
def form_valid(self, form):
# some actions...
return redirect(self.get_success_url())
erikbwork et moi-même avons eu le problème de ne pouvoir inclure qu'un seul modèle dans une vue basée sur une classe générique. J'ai trouvé une manière similaire de l'aborder comme Miao, mais plus modulaire.
J'ai écrit un Mixin pour que vous puissiez utiliser toutes les vues génériques basées sur les classes. Définissez le modèle, les champs et maintenant également child_model et child_field - et vous pourrez ensuite encapsuler les champs des deux modèles dans une balise comme Zach le décrit.
class ChildModelFormMixin:
''' extends ModelFormMixin with the ability to include ChildModelForm '''
child_model = ""
child_fields = ()
child_form_class = None
def get_child_model(self):
return self.child_model
def get_child_fields(self):
return self.child_fields
def get_child_form(self):
if not self.child_form_class:
self.child_form_class = model_forms.modelform_factory(self.get_child_model(), fields=self.get_child_fields())
return self.child_form_class(**self.get_form_kwargs())
def get_context_data(self, **kwargs):
if 'child_form' not in kwargs:
kwargs['child_form'] = self.get_child_form()
return super().get_context_data(**kwargs)
def post(self, request, *args, **kwargs):
form = self.get_form()
child_form = self.get_child_form()
# check if both forms are valid
form_valid = form.is_valid()
child_form_valid = child_form.is_valid()
if form_valid and child_form_valid:
return self.form_valid(form, child_form)
else:
return self.form_invalid(form)
def form_valid(self, form, child_form):
self.object = form.save()
save_child_form = child_form.save(commit=False)
save_child_form.course_key = self.object
save_child_form.save()
return HttpResponseRedirect(self.get_success_url())
Exemple d'utilisation:
class ConsumerRegistrationUpdateView(UpdateView):
model = Registration
fields = ('firstname', 'lastname',)
child_model = ConsumerProfile
child_fields = ('payment_token', 'cart',)
Ou avec ModelFormClass:
class ConsumerRegistrationUpdateView(UpdateView):
model = Registration
fields = ('firstname', 'lastname',)
child_model = ConsumerProfile
child_form_class = ConsumerProfileForm
Terminé. J'espère que cela aide quelqu'un.
Vous pouvez vérifier ma réponse ici pour un problème similaire.
Il explique comment combiner l'enregistrement et le profil utilisateur en un seul formulaire, mais il peut être généralisé à n'importe quelle combinaison ModelForm.
Vous devriez probablement jeter un œil à Inline Formsets . Les jeux de formulaires en ligne sont utilisés lorsque vos modèles sont liés par une clé étrangère.