Czy ModelForm
w django można umieścić wiele modeli w jednym ? Próbuję utworzyć formularz edycji profilu. Muszę więc dołączyć niektóre pola z modelu User i modelu UserProfile. Obecnie używam 2 takich formularzy
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")
Czy istnieje sposób na skonsolidowanie ich w jeden formularz, czy po prostu muszę utworzyć formularz i samodzielnie obsłużyć ładowanie i zapisywanie bazy danych?
python
django
django-forms
Jason Webb
źródło
źródło
Odpowiedzi:
Możesz po prostu pokazać oba formularze w szablonie wewnątrz jednego
<form>
elementu HTML. Następnie po prostu przetwarzaj formularze oddzielnie w widoku. Nadal będziesz mógł używaćform.save()
i nie musisz samodzielnie przetwarzać ładowania i zapisywania bazy danych.W tym przypadku nie powinieneś tego potrzebować, ale jeśli zamierzasz używać formularzy z tymi samymi nazwami pól, zajrzyj do
prefix
kwarg dla formularzy django. (Odpowiedziałem na pytanie na ten temat tutaj ).źródło
<form>
element?FormView
maform_class
przypisany tylko jeden .TemplateView
i zaimplementuj tę samą logikę co FormView, ale z wieloma formularzami.Możesz spróbować użyć tego fragmentu kodu:
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
Przykładowe zastosowanie:
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())
źródło
admin.E016) The value of 'form' must inherit from 'BaseModelForm'.
UpdateView
?Erikbwork i ja mieliśmy problem polegający na tym, że do ogólnego widoku opartego na klasach można dołączyć tylko jeden model. Znalazłem podobny sposób podejścia do tego jak Miao, ale bardziej modułowy.
Napisałem Mixin, abyś mógł używać wszystkich ogólnych widoków opartych na klasach. Zdefiniuj model, pola, a teraz także child_model i child_field - a następnie możesz zawijać pola obu modeli w tagu, jak opisuje Zach.
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())
Przykładowe zastosowanie:
class ConsumerRegistrationUpdateView(UpdateView): model = Registration fields = ('firstname', 'lastname',) child_model = ConsumerProfile child_fields = ('payment_token', 'cart',)
Lub z ModelFormClass:
class ConsumerRegistrationUpdateView(UpdateView): model = Registration fields = ('firstname', 'lastname',) child_model = ConsumerProfile child_form_class = ConsumerProfileForm
Gotowe. Mam nadzieję, że to komuś pomoże.
źródło
save_child_form.course_key = self.object
jest.course_key
?return self.child_form_class(**self.get_form_kwargs())
ale pobiera niewłaściwą instancję modelukwargs['instance']
, np. Instancja jest modelem głównym, a nie modelem podrzędnym. Aby to naprawić, musisz najpierw zapisać kwargs w zmiennej,kwargs = self.get_form_kwargs()
a następnie zaktualizowaćkwargs['initial']
odpowiednią instancję modelu przed wywołaniemreturn self.child_form_class(**kwargs)
. W moim przypadku tak było,kwargs['instance'] = kwargs['instance'].profile
jeśli to ma sens.model_forms
?Prawdopodobnie powinieneś przyjrzeć się zestawom formularzy Inline . Zestawy formularzy wbudowanych są używane, gdy modele są powiązane kluczem obcym.
źródło
Tutaj możesz sprawdzić moją odpowiedź na podobny problem.
Mówi o tym, jak połączyć rejestrację i profil użytkownika w jeden formularz, ale można go uogólnić na dowolną kombinację ModelForm.
źródło
Użyłem Django betterforms „s wielopostaciowe i MultiModelForm w moim projekcie. Kod można jednak ulepszyć. Na przykład, jest zależny od django.six, który nie jest obsługiwany przez 3. +, ale wszystko to można łatwo naprawić
To pytanie pojawiło się kilka razy w StackOverflow, więc myślę, że nadszedł czas, aby znaleźć ustandaryzowany sposób radzenia sobie z tym.
źródło