在单个Django ModelForm中使用多个模型?

114 投票
6 回答
94398 浏览
提问于 2025-04-15 22:22

在Django中,能不能把多个模型放到一个ModelForm里面呢?我想做一个个人资料编辑的表单,所以需要同时包含用户模型(User model)和用户资料模型(UserProfile model)的一些字段。目前我用的是两个表单,像这样:

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

有没有办法把这两个表单合并成一个,还是说我只能自己创建一个表单,然后手动处理数据库的加载和保存呢?

6 个回答

5

我在我的项目中使用了 django betterformsMultiForm 和 MultiModelForm。不过,代码还有改进的空间。例如,它依赖于 django.six,而这个在 3.+ 版本中是不支持的,但这些问题都能很容易地解决。

这个问题在 StackOverflow 上出现过 好几次 ,所以我觉得是时候找一个标准的方法来处理这个问题了。

12

你可以试试这段代码:

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

使用示例:

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

你可以在一个 <form> 的 HTML 元素里面同时显示两个表单。然后在视图中分别处理这两个表单。这样你仍然可以使用 form.save(),不需要自己去处理数据库的加载和保存。

在这种情况下你可能不需要这样做,但如果你要使用的表单有相同的字段名称,可以看看 Django 表单中的 prefix 参数。我在这里回答过一个关于这个的问题 这里

撰写回答