Nhiều mô hình trong một mô hình django duy nhất?


96

Có thể có nhiều mô hình được bao gồm trong một đơn ModelFormtrong django không? Tôi đang cố gắng tạo một biểu mẫu chỉnh sửa hồ sơ. Vì vậy, tôi cần bao gồm một số trường từ mô hình Người dùng mô hình UserProfile. Hiện tại tôi đang sử dụng 2 hình thức như thế này

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")

Có cách nào để hợp nhất chúng thành một biểu mẫu hay tôi chỉ cần tạo một biểu mẫu và xử lý việc tải db và tự lưu lại?


Câu trả lời:


92

Bạn chỉ có thể hiển thị cả hai biểu mẫu trong mẫu bên trong một <form>phần tử html. Sau đó, chỉ cần xử lý các biểu mẫu riêng biệt trong dạng xem. Bạn vẫn có thể sử dụng form.save()và không phải tự mình xử lý tải và lưu db.

Trong trường hợp này, bạn không nên cần nó, nhưng nếu bạn đang sử dụng các biểu mẫu có cùng tên trường, hãy xem prefixkwarg cho các biểu mẫu django. (Tôi đã trả lời một câu hỏi về nó ở đây ).


Đây là một lời khuyên tốt, nhưng có những trường hợp điều này không được áp dụng, ví dụ. biểu mẫu mô hình tùy chỉnh cho một bộ định dạng.
Wtower

8
Cách đơn giản để làm cho một dạng xem dựa trên lớp có khả năng hiển thị nhiều hơn một biểu mẫu và một mẫu sau đó kết hợp chúng thành cùng một <form>phần tử là gì?
jozxyqk

1
Nhưng bằng cách nào? Thông thường một FormViewchỉ có một duy nhất form_classđược gán cho nó.
erikbwork

@erikbwork Bạn không nên sử dụng FormView cho trường hợp này. Chỉ lớp con TemplateViewvà triển khai logic tương tự như FormView, nhưng với nhiều biểu mẫu.
moppag

10

Bạn có thể thử sử dụng đoạn mã này:

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

Cách sử dụng ví dụ:

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())

Ngoại hình như thế này không thể được sử dụng trong quản trị do một số kiểm tra rõ ràng:admin.E016) The value of 'form' must inherit from 'BaseModelForm'.
WhyNotHugo

Làm thế nào tôi có thể sử dụng nó với UpdateView?
Pavel Shlepnev

3

erikbwork và tôi đều gặp vấn đề là người ta chỉ có thể đưa một mô hình vào Chế độ xem dựa trên lớp chung. Tôi đã tìm thấy một cách tiếp cận nó tương tự như Miao, nhưng mô-đun hơn.

Tôi đã viết một Mixin để bạn có thể sử dụng tất cả các Chế độ xem Dựa trên Lớp chung. Xác định mô hình, các trường và bây giờ cũng là mô hình con và trường con - và sau đó bạn có thể bọc các trường của cả hai mô hình trong một thẻ như Zach mô tả.

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())

Cách sử dụng ví dụ:

class ConsumerRegistrationUpdateView(UpdateView):
    model = Registration
    fields = ('firstname', 'lastname',)
    child_model = ConsumerProfile
    child_fields = ('payment_token', 'cart',)

Hoặc với ModelFormClass:

class ConsumerRegistrationUpdateView(UpdateView):
    model = Registration
    fields = ('firstname', 'lastname',)
    child_model = ConsumerProfile
    child_form_class = ConsumerProfileForm

Làm xong. Hy vọng rằng sẽ giúp một ai đó.


Trong này save_child_form.course_key = self.object, là .course_keygì?
Adam Starrh 14/07/17

Tôi nghĩ rằng course_key là mô hình liên quan, trong trường hợp của tôi là "người dùng" như trong UserProfile.user là một backref, có thể tên trường đó nên được tùy chỉnh nếu nó là một mixin có thể sử dụng lại. Nhưng tôi vẫn gặp một vấn đề khác trong đó biểu mẫu con không thực sự được điền bằng dữ liệu ban đầu, tất cả các trường từ Người dùng đều được điền sẵn nhưng không dành cho UserProfile. Tôi có thể phải sửa điều đó trước.
robvdl

Vấn đề tại sao biểu mẫu con không được phổ biến là bởi vì trong phương thức get_child_form, nó gọi return self.child_form_class(**self.get_form_kwargs())nhưng nó nhận được trường hợp mô hình sai kwargs['instance'], ví dụ: ví dụ là mô hình chính chứ không phải mô hình con. Để khắc phục, bạn cần lưu kwargs vào một biến trước tiên, kwargs = self.get_form_kwargs()sau đó cập nhật kwargs['initial']với phiên bản mô hình chính xác trước khi gọi return self.child_form_class(**kwargs). Trong trường hợp của tôi, đó là kwargs['instance'] = kwargs['instance'].profilenếu điều này có ý nghĩa.
robvdl

Thật không may khi lưu, nó sẽ vẫn bị lỗi ở hai nơi, một nơi self.object chưa có trong form_valid, vì vậy nó ném ra một AttributeError và một trường hợp khác không có ở đó. Tôi không chắc liệu giải pháp này có được kiểm tra đầy đủ trước khi được đăng hay không, vì vậy có thể tốt hơn nếu đi với câu trả lời khác bằng cách sử dụng CombineFormBase.
robvdl

1
model_formsgì?
Granny Aching, 12/09/19

2

Bạn có thể nên xem các bộ định dạng nội tuyến . Các bộ định dạng nội tuyến được sử dụng khi các mô hình của bạn có liên quan với nhau bằng một khóa ngoại.


1
Các bộ định dạng nội tuyến được sử dụng khi bạn cần làm việc với mối quan hệ một đến nhiều. Chẳng hạn như một công ty mà bạn thêm nhân viên. Tôi đang thử kết hợp 2 bảng thành một biểu mẫu duy nhất. Đó là mối quan hệ 1-1.
Jason Webb

Việc sử dụng bộ định dạng Nội tuyến sẽ hiệu quả, nhưng có thể ít hơn lý tưởng. Bạn cũng có thể tạo một Mô hình xử lý mối quan hệ cho bạn, rồi sử dụng một biểu mẫu duy nhất. Chỉ cần có một trang với 2 biểu mẫu như được đề xuất trong stackoverflow.com/questions/2770810/… sẽ hoạt động.
John Percival Hackworth

2

Bạn có thể kiểm tra câu trả lời của tôi ở đây cho một vấn đề tương tự.

Nó nói về cách kết hợp đăng ký và hồ sơ người dùng thành một biểu mẫu, nhưng nó có thể được khái quát thành bất kỳ kết hợp ModelForm nào.


0

Tôi sử dụng django betterforms 's đa dạng và MultiModelForm trong dự án của tôi. Tuy nhiên, mã có thể được cải thiện. Ví dụ: nó phụ thuộc vào django.six, không được hỗ trợ bởi 3. +, nhưng tất cả những điều này có thể dễ dàng được sửa

Câu hỏi này đã xuất hiện vài lần trong StackOverflow, vì vậy tôi nghĩ đã đến lúc tìm ra một cách chuẩn hóa để đối phó với vấn đề này.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.