我正在构建一个支持票据跟踪应用程序,并且我想从一个页面创建一些模型。门票通过ForeignKey属于客户。 Notes也通过ForeignKey属于Tickets。我想选择一个客户(这是一个完整的单独项目)或者创建一个新客户,然后创建一个Ticket,最后创建一个分配给新票证的Note。
由于我是Django的新手,我倾向于迭代工作,每次尝试新功能。我玩过ModelForms,但我想隐藏一些字段并进行一些复杂的验证。看起来我正在寻找的控制级别要么需要使用formset,要么手工完成所有操作,完成一个繁琐的手工编码模板页面,我试图避免这种情况。
我有什么可爱的功能吗?有人使用formset有一个很好的参考或示例吗?我为他们花了整整一个周末的API文档,我仍然无能为力。如果我分解并手工编码所有内容,这是一个设计问题吗?
答案 0 :(得分:75)
使用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是自定义验证的文档。
答案 1 :(得分:63)
我前一天刚好处于同样的情况,这是我的2美分:
1)我发现可以说是单一形式的多个模型输入的最短和最简洁的演示:http://collingrady.wordpress.com/2008/02/18/editing-multiple-objects-in-django-with-newforms/。
简而言之:为每个模型创建一个表单,使用<form>
keyarg将它们提交到单个prefix
的模板中,并使视图句柄验证。如果存在依赖关系,请确保保存“父”
依赖之前的模型,并在提交保存“子”模型之前使用父键的外键。该链接有演示。
2)也许表格集可以打成这样做,但据我深入研究,表格集主要用于输入相同模型的多个,其中可能可选地与另一个模型/模型相关联通过外键。但是,似乎没有用于输入多个模型数据的默认选项,而这不是什么formset似乎意味着。
答案 2 :(得分:25)
我最近遇到了一些问题,并且想出了如何做到这一点。 假设你有三个类,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对象。
答案 3 :(得分:8)
来自MultiModelForm的django-betterforms
是一个方便的包装器,可以执行Gnudiff's answer中所述的操作。它将常规# 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
包装在一个类中,该类透明地(至少对于基本用法)用作单个表单。我从下面的文档中复制了一个例子。
{{1}}
答案 4 :(得分: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
答案 5 :(得分:2)
“我想隐藏一些字段并进行一些复杂的验证。”
我从内置的管理界面开始。
构建ModelForm以显示所需的字段。
使用表单中的验证规则扩展表单。通常这是clean
方法。
确保此部分运作良好。
完成此操作后,您可以远离内置管理界面。
然后,您可以在一个网页上使用多个部分相关的表单。这是一堆模板,可以在一个页面上显示所有表单。
然后你必须编写视图函数来读取和验证各种表单事物并进行各种对象保存()。
“如果我分解并手动编码所有内容,这是一个设计问题吗?”不,这只是很多时间没有太大的好处。
答案 6 :(得分:0)
根据Django文档,内联表单集用于此目的: &#34;内联表单集是模型表单集之上的一个小抽象层。这些简化了通过外键使用相关对象的情况&#34;。
请参阅https://docs.djangoproject.com/en/dev/topics/forms/modelforms/#inline-formsets