Python Django:使用表单在一个模板中创建多个模型

Python Django:使用表单在一个模板中创建多个模型,python,django,django-forms,Python,Django,Django Forms,我正在构建一个支持票证跟踪应用程序,并且有一些模型我想从一个页面创建。门票通过外键属于客户。票据也通过外键属于票据。我希望可以选择选择一个客户(这是一个完全独立的项目)或创建一个新客户,然后创建一个票据,最后创建一个分配给新票据的注释 因为我是Django的新手,所以我倾向于迭代工作,每次都尝试新特性。我已经使用过ModelForms,但我想隐藏一些字段并进行一些复杂的验证。看起来我想要的控制级别要么需要表单集,要么手工完成所有事情,再加上一个乏味的手工编码的模板页面,这是我试图避免的 有什么可

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

因为我是Django的新手,所以我倾向于迭代工作,每次都尝试新特性。我已经使用过ModelForms,但我想隐藏一些字段并进行一些复杂的验证。看起来我想要的控制级别要么需要表单集,要么手工完成所有事情,再加上一个乏味的手工编码的模板页面,这是我试图避免的

有什么可爱的地方我错过了吗?是否有人对使用表单集有很好的参考或示例?我为他们花了整整一个周末的时间在API文档上,我仍然一无所知。如果我将所有内容分解并手工编写代码,这是设计问题吗?

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

我从内置的管理界面开始

  • 构建ModelForm以显示所需的字段

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

    确保此部件工作正常

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

    然后,您可以在一个网页上随意使用多个部分相关的表单。这是一组模板,用于在单个页面上显示所有表单

    然后必须编写view函数来读取和验证各种表单内容,并执行各种对象保存()


    “如果我把所有东西都分解并手工编写代码,这是不是一个设计问题?”不,这只是花了很多时间,没有多少好处。

    这真的不难实现。假设你有表格A、B和C。你打印出每个表格和页面,现在你需要处理这篇文章

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

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

    我最近遇到了一些问题,刚刚想出了如何执行此操作。 假设您有三个类,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对象。

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

    1) 我发现这里可以说是以单一形式对多个模型条目进行的最短、最简洁的演示:

    简言之:为每个模型制作一个表单,使用
    前缀
    键arg在单个
    中将它们提交到模板,并具有视图句柄验证。如果存在依赖项,请确保保存“父项” 在依赖项之前建模,并在提交“子”模型的保存之前使用父项ID作为外键。链接中有演示


    2) 也许可以击败表单集来实现这一点,但就我所钻研的而言,表单集主要用于输入同一模型的多个,可以通过外键选择性地绑定到另一个模型。但是,似乎没有用于输入多个模型数据的默认选项,而formset似乎并非用于此目的。

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

    我是不是遗漏了什么

    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
    

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

    请参见

    from是一个方便的包装器,可以完成中所述的操作。它将常规的
    ModelForm
    s封装在一个类中,该类透明地(至少对于基本用法)用作一个表单。我从下面的文档中复制了一个示例

    # 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
    

    顺便说一句,我不认为表单集是解决您描述的问题的好方法。我总是用它们来表示模型的多个实例。例如,您有一个申请者表单,并且希望有3个引用。您可以创建一个具有3个引用模型实例的表单集。请注意,通过这样做,.is_valid()调用不会短路。如果要使其短路,则需要延迟调用.is_valid()函数,直到“and”为止。在本例中,如何设置模型B和C的外键以指向主模型?我只有两个模型要在同一表单上显示。但是我没有得到exclude=('primary',)语句。什么是主要的?如果有两种型号,客户配置和合同。合同具有CustomerConfig的外键。例如customer_config=models.ForeignKey('CustomerPartnerConfiguration')什么是“primary”?我不知道怎么做,因此不做it@orokusaki:你还要什么?这似乎描述了一个解决方案。还有什么要说的呢?这个问题很模糊,所以很难提供实际的代码。请提供改进建议,而不是抱怨。你有什么建议?在看到你的答案之前,先看一下《django Betterperforms》及其课程。他们的解决方案看起来非常好,但似乎已经有一段时间没有更新了。你还在用这个@jozxyqk吗?有什么问题吗?@enchance已经好几年了。当时我觉得这很方便,而且是更好的选择之一。如果你不太喜欢它,它可以节省一些时间。我可以想象,当你想开始定制和制作非琐碎的表单时,你会更容易推出自己的表单。在视图中轻松地混合表单和上下文是我在django中错过的第一个功能。谢谢回复人。我在考虑用叉子叉