Django:使用形式在一个模板中的多个模型


114

我正在构建一个支持票证跟踪应用程序,并希望从一个页面创建一些模型。票证通过ForeignKey属于客户。注释也通过ForeignKey属于票证。我想选择一个客户(这是一个单独的项目)或创建一个新客户,然后创建一个工单,最后创建一个分配给新工单的便笺。

由于我是Django的新手,因此我倾向于反复工作,每次尝试新功能。我玩过ModelForms,但是我想隐藏一些字段并进行一些复杂的验证。似乎我正在寻找的控制级别需要表单集或手动完成所有操作,并完成一个繁琐的手工编码模板页面,而我试图避免这种情况。

我缺少一些可爱的功能吗?有人对使用表单集有很好的参考或示例吗?我花了整个周末为他们准备API文档,但我仍然一无所知。如果我分解并手工编码所有内容,这是设计问题吗?


首先,您应该验证客户表单,如果有效,请从request.POST(new_data = request.POST.copy())创建一个副本,然后获取客户ID(从已验证的客户表单中)并更新new_data,客户id表示外键字段(可能是您模型中的客户)的值。最后考虑使用new_data来验证您的第二个表单(门票)
Negar37

Answers:


87

使用ModelForms实在不是太难。假设您有A,B和C表单。您打印出每个表单和页面,现在需要处理POST。

if request.POST():
    a_valid = formA.is_valid()
    b_valid = formB.is_valid()
    c_valid = formC.is_valid()
    # we do this since 'and' short circuits and we want to check to whole page for form errors
    if a_valid and b_valid and c_valid:
        a = formA.save()
        b = formB.save(commit=False)
        c = formC.save(commit=False)
        b.foreignkeytoA = a
        b.save()
        c.foreignkeytoB = b
        c.save()

是用于自定义验证的文档。


2
顺便说一句,我认为表单集不能很好地解决您所描述的问题。我总是用它们来表示一个模型的多个实例。例如,您有一个申请人表单,并且您希望获得3个引用,从而形成一个包含3个Reference模型实例的表单集。
贾森·克里斯塔

1
请注意,使用此方法,.is_valid()调用不会短路。如果要使其短路,则需要延迟调用.is_valid()函数,直到“与”为止。
Lie Ryan

66

我一天前也处于相同的情况,这是我的2美分:

1)我可以在这里找到单一形式的多个模型输入的最短,最简洁的演示:http : //collingrady.wordpress.com/2008/02/18/editing-multiple-objects-in-django-with-newforms/

简而言之:为每个模型创建一个表单<form>,使用prefixkeyarg 将它们都提交到模板中,并进行视图句柄验证。如果存在依赖关系,只需确保在依赖关系之前保存“父”模型,并在提交“子”模型的保存之前使用父代的ID作为外键。链接中有演示。

2)也许表单集可打成了这样做,但据我的钻研,表单集主要用于输入相同的模型,它的倍数可能 /模型由外键可以任意捆绑到另一种模式。但是,似乎没有默认选项可输入多个模型的数据,而这并不是表单集的含义。


26

我最近遇到了一些问题,只是想出了解决方法。假设您有三个类,Primary,B,C,并且B,C具有Primary的外键

    class PrimaryForm(ModelForm):
        class Meta:
            model = Primary

    class BForm(ModelForm):
        class Meta:
            model = B
            exclude = ('primary',)

    class CForm(ModelForm):
         class Meta:
            model = C
            exclude = ('primary',)

    def generateView(request):
        if request.method == 'POST': # If the form has been submitted...
            primary_form = PrimaryForm(request.POST, prefix = "primary")
            b_form = BForm(request.POST, prefix = "b")
            c_form = CForm(request.POST, prefix = "c")
            if primary_form.is_valid() and b_form.is_valid() and c_form.is_valid(): # All validation rules pass
                    print "all validation passed"
                    primary = primary_form.save()
                    b_form.cleaned_data["primary"] = primary
                    b = b_form.save()
                    c_form.cleaned_data["primary"] = primary
                    c = c_form.save()
                    return HttpResponseRedirect("/viewer/%s/" % (primary.name))
            else:
                    print "failed"

        else:
            primary_form = PrimaryForm(prefix = "primary")
            b_form = BForm(prefix = "b")
            c_form = Form(prefix = "c")
     return render_to_response('multi_model.html', {
     'primary_form': primary_form,
     'b_form': b_form,
     'c_form': c_form,
      })

此方法应允许您执行所需的任何验证,以及在同一页面上生成所有三个对象。我还使用了JavaScript和隐藏字段来允许在同一页面上生成多个B,C对象。


3
在此示例中,如何设置模型B和C的外键以指向主模型?
用户

我只有两个要在同一表格上显示的模型。但是我没有得到exclude =('primary',)语句。什么是主要的?如果有2个模型CustomerConfig和Contract。合同具有CustomerConfig的外键。例如customer_config = models.ForeignKey('CustomerPartnerConfiguration')什么是“主要”?
pitchblack408 2014年

10

来自的MultiModelFormdjango-betterforms是一个方便的包装程序,可以执行Gnudiff的答案中所述。它将regulars包装ModelForm在单个类中,该类透明(至少对于基本用法而言)用作单个形式。我从下面的文档中复制了一个示例。

# forms.py
from django import forms
from django.contrib.auth import get_user_model
from betterforms.multiform import MultiModelForm
from .models import UserProfile

User = get_user_model()

class UserEditForm(forms.ModelForm):
    class Meta:
        fields = ('email',)

class UserProfileForm(forms.ModelForm):
    class Meta:
        fields = ('favorite_color',)

class UserEditMultiForm(MultiModelForm):
    form_classes = {
        'user': UserEditForm,
        'profile': UserProfileForm,
    }

# views.py
from django.views.generic import UpdateView
from django.core.urlresolvers import reverse_lazy
from django.shortcuts import redirect
from django.contrib.auth import get_user_model
from .forms import UserEditMultiForm

User = get_user_model()

class UserSignupView(UpdateView):
    model = User
    form_class = UserEditMultiForm
    success_url = reverse_lazy('home')

    def get_form_kwargs(self):
        kwargs = super(UserSignupView, self).get_form_kwargs()
        kwargs.update(instance={
            'user': self.object,
            'profile': self.object.profile,
        })
        return kwargs

刚刚看到django-betterforms它的MultiModelForm类,然后才遇到您的答案。他们的解决方案看上去确实不错,但似乎已经有一段时间没有更新了。您还在使用此@jozxyqk吗?任何问题?
enchance

@enchance已经好几年了。那时,我发现它很方便,也是周围较好的选择之一。如果您不太喜欢它,可以节省一些时间。我可以想象,当您要开始自定义并制作非平凡的表单时,自己动手做起来会更容易。我真的认为我在django中错过的第一个功能是在视图中轻松混合表单和上下文。
jozxyqk

感谢您的回复。我正在考虑仅对其进行分叉,并且可能会在此过程中进行一些更新。从目前为止我所看到的,它工作得很好。没错,这可以节省大量时间。
enchance

5

我目前有一个解决方法功能(它通过了我的单元测试)。当您只想从其他模型中添加有限数量的字段时,这是一个很好的解决方案。

我在这里想念什么吗?

class UserProfileForm(ModelForm):
    def __init__(self, instance=None, *args, **kwargs):
        # Add these fields from the user object
        _fields = ('first_name', 'last_name', 'email',)
        # Retrieve initial (current) data from the user object
        _initial = model_to_dict(instance.user, _fields) if instance is not None else {}
        # Pass the initial data to the base
        super(UserProfileForm, self).__init__(initial=_initial, instance=instance, *args, **kwargs)
        # Retrieve the fields from the user model and update the fields with it
        self.fields.update(fields_for_model(User, _fields))

    class Meta:
        model = UserProfile
        exclude = ('user',)

    def save(self, *args, **kwargs):
        u = self.instance.user
        u.first_name = self.cleaned_data['first_name']
        u.last_name = self.cleaned_data['last_name']
        u.email = self.cleaned_data['email']
        u.save()
        profile = super(UserProfileForm, self).save(*args,**kwargs)
        return profile

3

“我想隐藏一些字段并进行一些复杂的验证。”

我从内置的管理界面开始。

  1. 构建ModelForm以显示所需的字段。

  2. 用表单中的验证规则扩展表单。通常这是一种clean方法。

    确保这部分工作正常。

完成此操作后,您可以离开内置的管理界面。

然后,您可以在单个网页上四处查找与部分相关的表单。这是一堆模板材料,可在一个页面上显示所有表单。

然后,您必须编写视图函数来读取和验证各种表单内容,并执行各种对象saves()。

“如果我分解并手动编码所有内容,这是设计问题吗?” 不,只是很多时间而没有太多好处。


我不知道怎么做,所以不要这样做
orokusaki 2011年

1
@orokusaki:您还要什么?这似乎描述了一个解决方案。还有什么要说的?这个问题含糊不清,因此很难提供实际的代码。与其抱怨,不如提出改进建议。你有什么建议?
S.Lott

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.