Django:使用表单在一个模板中的多个模型
我正在build立一个支持票据跟踪应用程序,并有一些我想从一个页面创build的模型。 门票通过ForeignKey属于一个客户。 Notes也通过ForeignKey属于Tickets。 我想要select一个客户(这是一个单独的项目),或者创build一个新的客户,然后创build一个票据,最后创build一个分配给新票据的票据。
由于我对Django相当陌生,所以我倾向于迭代地工作,每次尝试新的function。 我玩过ModelForms,但我想隐藏一些字段并做一些复杂的validation。 看起来像我正在寻找的控制级别要么需要formset,要么手工完成,完成一个繁琐的,手工编码的模板页面,我试图避免。
有一些可爱的function,我错过了? 有人有一个很好的参考或使用formset的例子吗? 我花了整整一个周末的API文档,我仍然无能为力。 如果我分解并手工编码所有东西,这是否是一个devise问题?
这对于使用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()
这里是自定义validation的文档。
我前一天的情况大致相同,下面是我的2美分:
1)我在这里发现了多个模型入口的最短和最简明的演示: http : //collingrady.wordpress.com/2008/02/18/editing-multiple-objects-in-django-with-newforms/ 。
简而言之:为每个模型创build一个<form>
,使用prefix
keyarg将它们都提交到模板中,并使用prefix
keyarg进行视图处理validation。 如果存在依赖性,只要确保在依赖之前保存“父”模型,并在提交“子”模型保存之前使用父母的ID作为外键。 链接有演示。
2)也许formset可以被打成这样做,但据我深入研究,formset主要是input同一模型的多个,可以select绑定到另一个模型/模型的外键。 但是,input多个模型的数据似乎没有默认的选项,这似乎并不意味着什么。
我最近有一些问题,只是想出了如何做到这一点。 假设你有三个类,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, })
这种方法应该允许你做任何你需要的validation,以及在同一个页面上生成所有三个对象。 我也使用JavaScript和隐藏的字段,以允许在同一页面上生成多个B,C对象。
来自django-betterforms
的MultiModelForm是一个方便的包装来完成Gnudiff的答案中所描述的内容 。 它将常规的ModelForm
封装在一个单独的类中,这个类透明地(至less用于基本用法)被用作一个单独的表单。 我从下面的文档复制了一个例子。
# 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
我目前有一个解决方法function(它通过我的unit testing)。 当你只想添加其他模型的有限数量的字段时,这是一个很好的解决scheme。
我在这里错过了什么?
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
“我想隐藏一些领域,做一些复杂的validation。”
我从内置的pipe理界面开始。
-
构buildModelForm以显示所需的字段。
-
使用表单中的validation规则扩展窗体。 通常这是一个
clean
方法。确保这部分工作相当好。
一旦完成,您可以离开内置的pipe理界面。
然后,你可以在单个网页上混合多个部分相关的表单。 这是一个模板的东西来呈现在一个页面上的所有表单。
然后你必须写视图函数来读取和validation各种forms的东西,并做各种对象保存()。
“如果我分解并手工编码所有东西,这是否是一个devise问题? 不,这只是很多时间没有太多的好处。
根据Django的文档,内联表单就是为了这个目的:“内联表单是模型表单之上的一个小抽象层,它简化了通过外键处理相关对象的情况”。
请参阅https://docs.djangoproject.com/en/dev/topics/forms/modelforms/#inline-formsets