Formset(表单集)是多个表单的集合。Formset在Web开发中应用很普遍,它可以让用户在同一个页面上提交多张表单,一键添加多个数据,比如一个页面上添加多个用户信息。今天小编我就介绍下Django Formset的基础知识,Formset的分类以及如何使用Formset。

为什么要使用Django Formset

我们先来下看下Django中不使用Formset情况下是如何在同一页面上一键提交2张或多张表单的。我们在模板中给每个表单取不同的名字,如form1和form2(如下面代码所示)。注: form1和form2分别对应forms.py里的Form1()和Form2()。

<form >{{ form1.as_p }}{{ form2.as_p }}
</form>

用户点击提交后,我们就可以在视图里了对用户提交的数据分别处理。

if request.method == 'POST':form1 = Form1( request.POST,prefix="form1")form2 = Form2( request.POST,prefix="form2")if form1.is_valid() or form2.is_valid(): pass
else:form1 = Form1(prefix="form1")form2 = Form2(prefix="form2")

这段代码看似并不复杂,然而当表单数量很多或不确定时,这个代码会非常冗长。我们希望能控制表单的数量,这是我们就可以用Formset了。

Formset的分类

Django针对不同的formset提供了3种方法: formset_factory, modelformset_factory和inlineformset_factory。我们接下来分别看下如何使用它们。

如何使用formset_factory

对于继承forms.Form的自定义表单,我们可以使用formset_factory。我们可以通过设置extra和max_num属性来确定我们想要展示的表单数量。注意: max_num优先级高于extra。比如下例中,我们想要显示3个空表单(extra=3),但最后只会显示2个空表单,因为max_num=2。

from django import formsclass BookForm(forms.Form):name = forms.CharField(max_length=100)title = forms.CharField()pub_date = forms.DateField(required=False)# forms.py - build a formset of booksfrom django.forms import formset_factory
from .forms import BookForm# extra: 想要显示空表单的数量
# max_num: 表单显示最大数量,可选,默认1000BookFormSet = formset_factory(BookForm, extra=3, max_num=2)

在视图文件views.py里,我们可以像使用form一样使用formset。

# views.py - formsets example.
from .forms import BookFormSet
from django.shortcuts import renderdef manage_books(request):if request.method == 'POST':formset = BookFormSet(request.POST, request.FILES)if formset.is_valid():# do something with the formset.cleaned_datapasselse:formset = BookFormSet()return render(request, 'manage_books.html', {'formset': formset})

模板里可以这样使用formset。

<form action=”.” method=”POST”>
{{ formset }}
</form>

也可以这样使用。

<form method="post">{{ formset.management_form }}<table>{% for form in formset %}{{ form }}{% endfor %}</table>
</form>

如何使用modelformset_factory

Formset也可以直接由模型model创建,这时你需要使用modelformset_factory。你可以指定需要显示的字段和表单数量。

from django.forms import modelformset_factory
from myapp.models import AuthorAuthorFormSet = modelformset_factory(Author, fields=('name', 'title'), extra = 3)

当然上面方法我并不推荐,因为对单个表单添加验证方法非常不方便。我更喜欢的方式先创建自定义的ModelForm,添加单个表单验证,然后再利用modelformset_factory创建formset。

class AuthorForm(forms.ModelForm):class Meta:model = Authorfields = ('name', 'title')def clean_name(self):# custom validation for the name field...

由ModelForm创建formset:

AuthorFormSet = modelformset_factory(Author, form=AuthorForm)

在模板和视图里使用formset的方法与前面的例子是一样的。

如何使用inlineformset_factory

试想我们有如下recipe模型,Recipe与Ingredient是单对多的关系。一般的formset只允许我们一次性提交多个Recipe或多个Ingredient。但如果我们希望同一个页面上添加一个菜谱(Recipe)和多个原料(Ingredient),这时我们就需要用使用inlineformset了。

from django.db import modelsclass Recipe(models.Model):title = models.CharField(max_length=255)description = models.TextField()class Ingredient(models.Model):recipe = models.ForeignKey(Recipe, on_delete=models.CASCADE, related_name='ingredient')name = models.CharField(max_length=255)

利用inlineformset_factory创建formset的方法如下所示。该方法的第一个参数和第二个参数都是模型,其中第一个参数必需是ForeignKey。

# forms.py
from django.forms import ModelForm
from django.forms import inlineformset_factoryfrom .models import Recipe, Ingredient, Instructionclass RecipeForm(ModelForm):class Meta:model = Recipefields = ("title", "description",)IngredientFormSet = inlineformset_factory(Recipe, Ingredient, fields=('name',),extra=3, can_delete=False, max_num=5)

views.py中使用formset创建和更新recipe的代码如下。在对IngredientFormSet进行实例化的时候,必需指定recipe的实例。

def recipe_update(request, pk):recipe = get_object_or_404(Recipe, pk=pk)if request.method == "POST":form = RecipeForm(request.POST, instance=recipe)if form.is_valid():recipe = form.save()ingredient_formset = IngredientFormSet(request.POST, instance=recipe)if ingredient_formset.is_valid():ingredient_formset.save()return redirect('/recipe/')else:form = RecipeForm(instance=recipe)ingredient_formset = IngredientFormSet(instance=recipe)return render(request, 'recipe/recipe_update.html', {'form': form,'ingredient_formset': ingredient_formset,})def recipe_add(request):if request.method == "POST":form = RecipeForm(request.POST)if form.is_valid():recipe = form.save()ingredient_formset = IngredientFormSet(request.POST, instance=recipe)if ingredient_formset.is_valid():ingredient_formset.save()return redirect('/recipe/')else:form = RecipeForm()ingredient_formset = IngredientFormSet()return render(request, 'recipe/recipe_add.html', {'form': form,'ingredient_formset': ingredient_formset,})

模板recipe/recipe_add.html代码如下。

<h1>Add Recipe</h1>
<form action="." method="post">{% csrf_token %}{{ form.as_p }}<fieldset><legend>Recipe Ingredient</legend>{{ ingredient_formset.management_form }}{{ ingredient_formset.non_form_errors }}{% for form in ingredient_formset %}{{ form.name.errors }}{{ form.name.label_tag }}{{ form.name }}</div>{% endfor %}</fieldset><input type="submit" value="Add recipe" class="submit" />
</form>

最后的效果如下图所示:

整个formset的验证

formset由多个表单组成,单个表单的验证可以通过自定义的clean方法来完成,然而有时我们需要对整个formset的数据进行验证。一个常见例子就是去重。

比如下面例子中用户一次性提交多篇文章标题后,我们需要检查title是否已重复。我们先定义一个BaseFormSet,然后使用formset=BaseArticleFormSet添加formset的验证。

from django.forms import BaseFormSet
from django.forms import formset_factory
from myapp.forms import ArticleFormclass BaseArticleFormSet(BaseFormSet):def clean(self):"""Checks that no two articles have the same title."""if any(self.errors):returntitles = []for form in self.forms:title = form.cleaned_data['title']if title in titles:raise forms.ValidationError("Articles in a set must have distinct titles.")titles.append(title)ArticleFormSet = formset_factory(ArticleForm, formset=BaseArticleFormSet)

给Formset添加额外字段

在BaseFormSet里我们不仅可以添加formset的验证,而且可以添加额外的字段,如下所示:

from django.forms import BaseFormSet
from django.forms import formset_factory
from myapp.forms import ArticleFormclass BaseArticleFormSet(BaseFormSet):def add_fields(self, form, index):super().add_fields(form, index)form.fields["my_field"] = forms.CharField()ArticleFormSet = formset_factory(ArticleForm, formset=BaseArticleFormSet)

小结

Formset真的非常有用,属于Django必备的基础知识之一。使用的时候先定义单个的form,然后利用factory生成formset。你需要根据不同应用场景选择不同的formset,并了解如何进行formset的验证。希望本文对你有所帮助。原创不易,欢迎点赞转发。

接下来我会讲下Django的Permission系统,欢迎关注我的微信号。

Django Web开发核心基础知识

Django网站开发四件套是如何遵循MVC软件设计模式的?

Django基础核心技术介绍(1): Model模型的介绍与设计

Django基础核心技术介绍(2): URL的设计与配置

Django基础核心技术介绍(3): View视图详解与通用视图

Django基础核心技术介绍(4): Template模板的编写及过滤器

Django基础核心结束介绍(5): Forms表单的使用与设计

Django基础(6): 模型Models高级进阶必读。

Django基础(7): cookie和session应用场景及如何使用

Django基础(8): 缓存Cache应用场景及工作原理,Cache设置及如何使用

Django基础(9): 表单Forms的高级使用技巧

Django基础(10): URL重定向的HttpResponseDirect, redirect和reverse的用法

Django基础(11): 表单集合Formset的高级用法

Django基础(12): Request对象详解及开发显示用户IP地址和浏览器APP

Django基础(13): 深夜放干货。QuerySet特性及高级使用技巧,如何减少数据库的访问,节省内存,提升网站性能。

Django基础(14): 通过next参数实现登录后跳转回到前一页的3种方法

Django基础(15): 模板过滤器(filter)的工作原理及如何自定义模板过滤器

Django基础(16): 模板标签(tags)的分类及如何自定义模板标签

Django基础(17): 如何上传处理文件及Ajax文件上传示范(附GitHub源码)

Django基础(18): 实现文件下载的3种方法及文件私有化

Django基础(19): Django Admin管理后台详解(上)

Django基础(20): Django admin管理后台详解(中)如何自定义list_display和list_filter

Django基础(21): Django admin管理后台详解(下)如何自定义actions, 表单和美化admin

Django基础(22): 数据库的设计之自定义表名,建立索引和使用多数据库主从配置

Django基础(23): 权限管理(permissions)与用户组(group)详解

Django基础(24): aggregate和annotate方法使用详解与示例

Django基础(25):settings.py设置选项深入解读。大江狗精品原创。

Django基础(26): 常用装饰器应用场景及正确使用方法

Django基础(27): 快捷函数(shortcut function)模块详解Django基础(28): 如何设计充满陷阱的优美URL

Django基础(28): 如何设计充满陷阱的优美URL

Django Web开发实战案例

Django 2.0 项目实战(1): 扩展Django自带User模型,实现用户注册与登录

Django 2.0 项目实战(2): 编辑用户个人资料,扩展Django后台UserAdmin

Django 2.0项目实战(3): 密码重置与退出登录

Django 2.0 项目实战: 图片上传与显示

Django 2.0 项目实战: PDF文件页面提取

Django 2.0 项目实战: PDF文件合并

Django 2.0 项目实战:输出树形分类目录

Django 2.0 项目实战: 网页计数器统计浏览次数

Django 2.0 项目实战:  利用AJAX实现博文实时搜索

Django 1.X和2.0下利用自带分页Paginator类实现分页功能

Django实战: 利用Ajax生成联动下拉菜单

世界那么大,我想去看看。Django仿制微信朋友圈九宫格相册(1)

世界那么大,我想去看看。Django仿制微信朋友圈九宫格相册(2)

django-allauth教程(1): 安装,用户注册,登录,邮箱验证和密码重置(更新)

django-allauth教程(2): 用户个人资料UserProfile扩展与编辑

django-allauth教程(3): 第三方账户授权登录(以百度账号为例)

django-allauth教程(4): 美化模板,自定义邮件和消息内容

Django+jQuery cropper实现用户头像裁剪, 预览和上传[原创]

Django实战教程: 开发餐厅在线点评网站(1)

Django实战教程: 开发餐厅在线点评网站(2)

Django实战教程: 开发企业级应用智能文档管理系统smartdoc(1)

Django实战教程: 开发企业级应用智能文档管理系统smartdoc(2)之权限管理

Django实战教程: 开发企业级应用智能文档管理系统smartdoc(3)附GitHub代码地址

Django实战专题: 开发专业博客(1)之内容管理后台开发

Django实战专题: 开发专业博客(2)之母子类别导航和添加富文本编辑器CKEditor

Django实战专题: 开发专业博客(3)之仿微信评论点赞功能

Django实战: Python爬取链家上海二手房信息,存入数据库并在前端显示

Django应用实战: 编写你自己的PDF编辑器, 实现PDF页面提取, 页面合并与替换。

如何在阿里云Ubuntu服务器通过uWSGI和Nginx部署Django项目教程-大江狗原创出品

Django Web开发学习笔记

浅谈Django Model创建对象的save与create方法

Django模板设置全局变量(默认变量)

Django常用命令django-admin.py和manage.py用法详解

Django自定义图片和文件上传路径(upload_to)的2种方式

Django ContentTypes框架详解及使用场景介绍

Django更改模型过程中易出现的问题及解决方案

2019新年第一篇: SQLite的优缺点及Django配置MySQL数据库

Django基础(11): 表单集合Formset的高级用法详解相关推荐

  1. mvc ajax提交html标签,Mvc提交表单的四种方法全程详解

    Mvc提交表单的四种方法全程详解 2019-01-05 编程之家 https://www.jb51.cc 编程之家收集整理的这篇文章主要介绍了Mvc提交表单的四种方法全程详解,编程之家小编觉得挺不错的 ...

  2. 功能表单之人员构造器字段类型详解—JEPLUS软件快速开发平台

    为什么80%的码农都做不了架构师?>>>    JEPLUS功能表单之人员构造器字段类型详解 大家平时在开发过程中会遇到各种各样的选择人员的操作,比如负责人,登记人,当前操作人,部门 ...

  3. 【JS基础】var formdata=new FormData() 【FormData用法详解 】

    FormData用法详解 简介 FormData 对象的使用: 1.用一些键值对来模拟一系列表单控件:即将form 中表单元素的 name 与 value 组装成一个 queryString 2.异步 ...

  4. html form 提交json数据格式,表单提交时编码类型enctype详解

    很早以前,当还没有前端这个概念的时候,我在写表单提交完全不去理会表单数据的编码,在action属性里写好目标URL,剩下的啊交给浏览器吧~但是现在,更多时候我们都采用Ajax方式提交数据,这种原始的方 ...

  5. 表单提交对数据进行加密详解(RSA加密)

    一.使用背景 以前在做项目的时候就觉得表单直接明文提交非常不安全.有心之人只要稍加操作就能轻易获取用户的信息,这是非常可怕的事情,但又苦于不知道该如何加密,如何提高安全性. 后来慢慢接触到RSA,才知 ...

  6. php表单确认密码,jQuery表单验证之密码确认实例详解

    本文主要为大家详细介绍了jQuery表单验证之密码确认的实现代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能帮助到大家. 众所周知,在修改密码时会需要两次输入密码.这时如何确保两个密码框 ...

  7. 微信小程序中form 表单提交和取值实例详解

    2019独角兽企业重金招聘Python工程师标准>>> 我们知道,如果我们直接给 input 添加 bindinput,比如:<input bindinput="on ...

  8. 【零基础】极星9.3止盈止损用法详解

    交流Q群1064240775 一.前言 止盈止损是期货交易中比较基础的部分了,但从设置上来说还是有点复杂性的,所以这篇就好好写明白极星9.3的止盈止损. 二.为啥会有止盈止损 首先得搞明白,为啥会有止 ...

  9. Java基础篇(04):日期与时间API用法详解

    本文源码:GitHub·点这里 || GitEE·点这里 一.时间和日期 在系统开发中,日期与时间作为重要的业务因素,起到十分关键的作用,例如同一个时间节点下的数据生成,基于时间范围的各种数据统计和分 ...

最新文章

  1. 16.04linux 安装微信,Ubuntu 16.04安装微信的过程记录
  2. range.clonecontents 不准确_家长注意!通州今起开展幼升小数据调查,不参加或影响明年入学...
  3. matlab imwrite函数保存jpg格式图像丢失数据或改变图像
  4. MybatisPlus实现分页
  5. js 简单弹框toast
  6. JVM02----垃圾收集上(堆)
  7. ActiveMQ 简单应用
  8. c++ socket线程池_Netty(3)——Reactor线程模型
  9. C++:实现量化基础互换利率basis swap rate helpers测试实例
  10. Epub格式电子书格式
  11. 熟女给老实木讷男孩的恋爱建议(转帖)
  12. 解剖点击量过亿的软文牛人是怎样炼成的?
  13. 猿人学web端爬虫攻防大赛赛题解析_第一题:源码乱码
  14. Linux常用快捷键、文件管理和查询
  15. Actions(鼠标事件)方法列表
  16. 28天打造专业红客(十一)
  17. HTML学习笔记(五)
  18. 信息管理毕设新颖题目
  19. win10python3.7怎么装pytorch_Win10下Pytorch安装、环境搭建、及报错的解决方案
  20. RELN抗体丨精准检测皮质层特异性标志物

热门文章

  1. 无师自通-自己学写脚本,小明外挂脚本代码教程 转载
  2. Mybatis-@MapperScan和mybatisscan分析
  3. Mac版OpenGL环境配置
  4. GIt部署到linux服务器
  5. 很小巧,很好用的js格式化工具
  6. 如何解决宝马汽车外接OBD接口设备触发报警的问题?
  7. day14:噪声的种类与生成
  8. 算法-寻找Top2的升序子数组
  9. 一个简单的软件需求规划书模版
  10. 软件需求工程--电网公司智慧减负系统