django中ModelForm多表单组合的解决方案

django是python语言快速实现web服务的大杀器,其开发效率能够很是的高!但由于秉承了语言的灵活性,django框架又太灵活,以致于想实现任何功能都有种“条条大路通罗马”的感受。这么多种选择放在一块儿,如何分出高下?我想此时的场景下就两个标准:一、相同的功能用最少的代码实现(代码少BUG也会少);二、相对最易于理解,从而易于维护和扩展。书归正传,web服务容许用户输入,基本上要靠表单。而django对表单的支持力度很是大,咱们用不着在浏览器端的html文件里写大量<form>代码,再到web端去匹配form里的id/name/value、验证规则,再与持久层数据库比较并作操做。咱们须要完成的工做很是少,能够没有类似的重复代码。有些复杂的场景,会要求一个表单的内容存放到多张表里,本文将经过4个部分,阐述它的实现方法。html

 

一、django基础表单的功能python

定义一个表单很是简单,继承类django.forms.Form便可,例如:web

 

 


这个表单类能够生成HTML形式的form,能够从request.POST中解析form到ProjectForm类实例。怎么作到的呢?数据库

 

看下django.forms.Form定义:django

 

 


注释说得很清楚,Form这个类就是为了实现declarative syntax的,也就是说,继承了Form后,咱们直观的表达ProjectForm里要有一个Field名叫name,不关心其语法实现,而经过Form多继承中的DeclarativeFieldsMetaclass语法糖,将会把name弄到类实例的self.fields里。浏览器

 

咱们重点关注表单的BaseForm类,它实现了基本的逻辑。截选了一小段对接下来的陈述有意义的代码,作一个简单的注释。并发

 

 


因此,基本表单的功能看BaseForm已经足够了。框架

 

 

二、从模型建立表单post

django对于MVC中的C与M间的映射是很是体贴的,集中体现中Model模型中(好比模型的权限与用户认证)。那么,一个模型表明着RDS中的一张表,模型的实例表明着关系数据库中的一行,而form如何与一行相对应呢?网站

定义一个模型引伸出的表单很是简单,例如:

 

 


在model中告诉django模型是谁,在fields中告诉django须要在表单中建立哪些字段。django会有一个django.db.models.Field到django.forms.Field的转换规则,此时会生成Form。咱们看看ModelForm是什么样的:

 

 

相似Form类,ModelFormMetaclass就是语法糖,咱们重点看BaseModelForm类:

 

 

 

 

因此,对于ModelForm咱们能够传入instance参数初始化表单,能够调用save()方法直接将从html里获得的表单数据持久化到数据库中。而咱们只须要几十行代码就能够完成这么多工做。

 

三、通用视图

django.views.generic.ListView和django.views.generic.edit下的CreateView, UpdateView, DeleteView都是通用视图。即,咱们又能够经过它们,把不少重复的工做交给django完成,又能够少写不少代码完成一样的功能了。这里仅以CreateView为例说明,由于它相对最复杂,接下来的多ModelForm的提交也是在CreateView上进行的。

通用视图使用时,只须要承继后,再设置model或者form_class便可。好比CreateView就会由django自动的把页面上POST出的form数据解析到model生成的表单(或者form_calss指定的ModelForm类型表单),同时调用表单的save方法将数据添加到模型对应的数据库表中。固然GET请求时会生成空form到页面上。能够看到,除去定义model或者form类外,几行代码就能够搞定这么多事。咱们看看CreateView的继承关系:

简单介绍下CreateView通用视图中每一个父类的做用。

 

  1. View是全部视图类的父类,根据方法名分发请求到具体的get或者post等方法,提供as_view方法。
  2. TemplateResponseMixin提供render_to_response方法将响应经过context上下文在模板上渲染。
  3. ContextMixin在context上下文中加入'view'元素,值为self实例。
  4. ProcessFormView在GET请求上渲染表单,在POST请求上解析form到表单实例。注意,它会在post请求中判断表单是否可用,is_valid为真时,会调用form_valid方法,所以,重写form_valid方法是第4部分处理多model到一个form的关键。
  5. FormMixin容许处理表单,可指定form_class为某个表单。
  6. SingleObjectMixin生成context上下文,同时根据model模型名称生成object并添加到上下文中的'object'元素。
  7. ModelFormMixin提供在请求中处理modelform的方式。
  8. SingleObjectTemplateResponseMixin帮助TemplateResponseMixin提供模板。

 

因此,在用CreateView、一个模型、一个模板实现添加一行记录的功能时是多么简单,由于这些父类会自动生成object,渲染到模板,解析form表单,save到数据库中。因此,从模型建立出的表单ModelForm,配合上通用视图后,威力巨大!!

 

四、多个ModelForm在一个form里提交

终于能够回到本文的主题了。CreateView默认是处理一个Model模型、一个ModelForm表单的,然而,不少时候为了解耦,会把一张表拆成多张表,经过id关联在一块儿。在django的模型中就体现为ForeignKey、ManyToManyField或者OneToOneField。而在业务逻辑上,须要体现为一张表单,对应着数据库里的多张表。

例如,咱们但愿录入合同,其中合同Model中还有地址Model和项目Model,而项目Model中又有地址Model,等等。

固然,咱们有不少种实现的方案,可是,前面三部分说了那么多,不是浪费口水的。咱们已经有了通用视图+ModelForm这样的利器,难道还须要手动去写Form表单?咱们已经习惯了在Model里定义好类型和有点注释做用还能当label的verbose_name,还须要在forms.Form里再来一遍?还须要在视图中写这么通用的逻辑代码吗?固然不用。

inlineformset_factory是一种方案,但它限制太多,并且有些晦涩,我我的感受是不太好用的。

那么,从第1部分我介绍的Form里的prefix,以及第3部分里类图中的ProcessFormView容许重定义form_valid,以及第2部分中ModelForm的save方法的行为控制,解决方案已经一目了然了。

拿上面提到的例子来讲,咱们建立合同时,指明了项目,包括项目地址和合同签定地址,这涉及到三张表和四条记录(地址表有两条)。

咱们三张表的模型以下:

 

 


接着,定义ModelForm表单,这很是简单:

 

 

 


再写视图,这里要重写2个方法:

 

 

 


最后写模板:

 

 

 


至此,咱们能够只用几十行代码就完成复杂的功能,代码逻辑也清晰可控。

 

从这篇文章里也能够看得出,django实在是快速开发网站的必备神器!固然,快速不表明不可以支撑大并发的应用,instagram这个很火的服务就是用django写的。因为python和django过于灵活,都将要求django的开发者们惟有更资深才能写出生产环境下的服务。

相关文章
相关标签/搜索