zoukankan      html  css  js  c++  java
  • django中ModelForm多表单组合的解决方案

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

    1、django基础表单的功能

    定义一个表单非常简单,继承类django.forms.Form即可,例如:

    
    


    这个表单类可以生成HTML形式的form,可以从request.POST中解析form到ProjectForm类实例。怎么做到的呢?

    看下django.forms.Form定义:

    
    


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

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

    
    


    所以,基本表单的功能看BaseForm已经足够了。

    2、从模型创建表单

    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里得到的表单数据持久化到数据库中。而我们只需要几十行代码就可以完成这么多工作。

    3、通用视图

    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,配合上通用视图后,威力巨大!!

    4、多个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的开发者们唯有更资深才能写出生产环境下的服务。

  • 相关阅读:
    27 Spring Cloud Feign整合Hystrix实现容错处理
    26 Spring Cloud使用Hystrix实现容错处理
    25 Spring Cloud Hystrix缓存与合并请求
    24 Spring Cloud Hystrix资源隔离策略(线程、信号量)
    23 Spring Cloud Hystrix(熔断器)介绍及使用
    22 Spring Cloud Feign的自定义配置及使用
    21 Spring Cloud使用Feign调用服务接口
    20 Spring Cloud Ribbon配置详解
    19 Spring Cloud Ribbon自定义负载均衡策略
    18 Spring Cloud Ribbon负载均衡策略介绍
  • 原文地址:https://www.cnblogs.com/zhanglixina/p/9584105.html
Copyright © 2011-2022 走看看