【问题标题】:Django: multiple models in one template using forms [closed]Django:使用表单的一个模板中的多个模型[关闭]
【发布时间】:2010-10-08 20:11:35
【问题描述】:

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

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

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

【问题讨论】:

  • 首先您应该验证您的客户表单,如果它有效,则从 request.POST ( new_data=request.POST.copy() ) 创建一个副本。然后获取客户 ID (来自经过验证的客户form )并通过更新 new_data ,使客户 id 成为外键字段的值(可能是您模型中的客户)。最后考虑使用 new_data 来验证您的第二个表单(Tickets)

标签: python django django-forms


【解决方案1】:

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

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

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

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

    确保这部分运行良好。

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

然后,您可以在一个网页上处理多个部分相关的表单。这是一堆模板,可以在一个页面上呈现所有表单。

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

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

【讨论】:

  • 我不知道怎么做,所以不要这样做
  • @orokusaki:你还想要什么?这似乎描述了一个解决方案。还应该说什么?这个问题很模糊,所以很难提供实际的代码。与其抱怨,不如提出改进建议。你有什么建议?
【解决方案2】:

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

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

【讨论】:

  • 顺便说一句,我不认为表单集可以很好地解决您描述的问题。我总是用它们来表示模型的多个实例。例如。您有一个申请表,并且想要 3 个引用您创建一个包含 3 个引用模型实例的表单集。
  • 请注意,按照您的操作方式,.is_valid() 调用不会短路。如果你想短路它,你需要延迟调用 .is_valid() 函数直到'and'。
【解决方案3】:

我最近遇到了一些问题,只是想出了如何做到这一点。 假设你有三个类,Primary、B、C,并且 B、C 有一个主外键

    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 对象。

【讨论】:

  • 在这个例子中,你如何设置模型 B 和 C 的外键指向主模型?
  • 我只有两个模型要显示在同一个表单上。但我没有得到 exclude = ('primary',) 声明。什么是初级?如果有 2 个模型 CustomerConfig 和 Contract。合同具有 CustomerConfig 的外键。比如customer_config=models.ForeignKey('CustomerPartnerConfiguration') 什么是'primary'?
【解决方案4】:

一天前我也遇到了同样的情况,这是我的 2 美分:

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

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

2) 也许表单集可以被打败,但据我深入研究,表单集主要用于输入同一模型的倍数,可能可以选择绑定到另一个模型/模型通过外键。但是,似乎没有用于输入多个模型数据的默认选项,而这似乎不是 formset 的用途。

【讨论】:

    【解决方案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
    

    【讨论】:

      【解决方案6】:

      根据 Django 文档,内联表单集用于此目的: “内联表单集是模型表单集之上的一个小型抽象层。这些简化了通过外键处理相关对象的情况”。

      https://docs.djangoproject.com/en/dev/topics/forms/modelforms/#inline-formsets

      【讨论】:

        【解决方案7】:

        来自django-betterformsMultiModelForm 是一个方便的包装器,用于执行Gnudiff's answer 中描述的操作。它将常规 ModelForms 包装在一个单独的类中,该类透明地(至少对于基本用法)用作单一形式。我从他们下面的文档中复制了一个示例。

        # 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 已经好几年了。那时我发现它很方便,并且是更好的选择之一。如果你不太喜欢它,它会节省一些时间。我可以想象当你想开始定制和做一些重要的表格时,你自己的表格会更容易。在视图中轻松混合表单和上下文是我真正认为我在 django 中错过的第一个功能。
        • 感谢您的回复。我正在考虑只是分叉它,并且可能会在此过程中更新一些东西。从我目前看到的情况来看,它工作得很好。你是对的,这可以节省大量时间。
        猜你喜欢
        • 2013-10-23
        • 2013-05-30
        • 2011-07-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-06-29
        • 2013-02-16
        相关资源
        最近更新 更多