Django: 在一个模板中使用表单的多个模型

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

由于我对 Django 相当陌生,我倾向于迭代工作,每次都尝试新的特性。我已经使用过 ModelForms,但是我想隐藏一些字段并进行一些复杂的验证。似乎我要寻找的控制级别要么需要格式集,要么需要手工完成所有事情,并且还要完成一个冗长的、手工编码的模板页面,而这正是我想要避免的。

我是不是漏掉了什么好东西?有人有使用格式集的好参考或例子吗?我花了整整一个周末研究他们的 API 文档,但我仍然毫无头绪。如果我把所有东西都手工编码,是不是设计上的问题?

125701 次浏览

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

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

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

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

    确保这部分工作得相当好。

一旦完成了这一步,您就可以离开内置的管理界面了。

然后你可以在一个网页上摆弄多个部分相关的表单。这是一堆模板内容,用于在单个页面上显示所有表单。

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

“如果我把所有东西都分解并手工编写代码,这会是一个设计问题吗?”没有,只是时间太长,收益不大。

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

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

我最近遇到了一些问题,刚刚想出了如何做到这一点。 假设你有三个类,小学,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 对象。

I just was in about the same situation a day ago, and here are my 2 cents:

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

简而言之: 为每个模型制作一个表单,将它们提交到单个 <form>模板中,使用 prefix keyarg 并进行视图句柄验证。如果存在依赖关系,只需确保保存“父” model before dependant, and use parent's ID for foreign key before commiting save of "child" model. The link has the demo.

2)也许格式集可以打败这样做,但就我深入研究,格式集主要是为了输入相同模型的倍数,可选地被外键绑定到另一个模型/模型。然而,似乎没有默认选项可以输入多个模型的数据,而且格式集似乎并不是这个意思。

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

我错过了什么吗?

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 文档,内联格式集就是为了这个目的: “ Inline formsets 是模型 formsets 之上的一个小抽象层。这简化了通过外键处理相关对象的情况”。

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

来自 django-betterformsMultiModelForm是一个方便的包装器,可以执行 Gnudiff 的回答中描述的操作。它将常规的 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