django formset实现数据表的批量操作的示例代码


Posted in Python onDecember 06, 2019

什么是formset

我们知道forms组件是用来做表单验证,更准确一点说,forms组件是用来做数据库表中一行记录的验证。有forms组件不同,formset是同科同时验证表中的多行记录,即formset是做表单批量验证的组件。

批量添加

首先要实例化formset对象,对象初始化时需要提供操作表的forms表单类,参数 extra 用来显示验证几行数据。将实例化的formset对象传递给前端页面,前端模板通过两层循环:第一层循环form,第二层循环form中的字段。当GET请求时,直接将实例化的formset对象传递给前端。当POST请求时,批量验证表单,当所有数据都没有问题时,后台数据库保存数据。

后台保存数据时,有两种方式:第一种方式简洁,但是无法捕获字段唯一约束的错误;因此使用formset批量添加数据时最好使用第二中方式,手动捕获唯一约束错误信息并交给formset送到前端页面显示。

models.Permission.objects.create(**row)
obj = models.Permission(**row) | obj.save()

唯一约束错误信息捕获的过程,需要使用 obj.validate_unique() 判断该对象是否满足唯一约束,如果不满足则通过异常捕获操作,捕获异常信息。通过 formset.errors[i].update(e) 把错误信息放入formset中送到前端页面显示。之所以这样做,是因为通过forms组件的验证时无法捕获唯一约束的错误。因此这里通过手动收集错误信息并放入forset中。

此外,如果前端页面渲染的表单没有填写数据,直接提交是不会报错的。 formset默认只要不改动字段就不会对该行数据做验证。只要填写一个字段,该行数据则会做表单验证 。

# views.py
def multi_add(request):
  """
  批量添加
  :param request:
  :return:
  """
  formset_class = formset_factory(MultiPermissionForm, extra=2)

  if request.method == 'GET':
    formset = formset_class()
    return render(request, 'multi_add.html', {'formset': formset})

  formset = formset_class(data=request.POST)
  if formset.is_valid():
    flag = True
    # 检查formset中没有错误信息,则讲用户提交的数据获取到。
    post_row_list = formset.cleaned_data 
    for i in range(0, formset.total_form_count()):
      row = post_row_list[i]
      if not row:
        continue
      try:
        obj = models.Permission(**row)
        obj.validate_unique() # 检查当前对象在数据库是否存在唯一的异常。
        obj.save()
      except Exception as e:
        formset.errors[i].update(e)
        flag = False
    if flag:
      return HttpResponse('提交成功')
    else:
      return render(request, 'multi_add.html', {'formset': formset})
  return render(request, 'multi_add.html', {'formset': formset})

前端模板通过两层循环:第一层循环formset得到每一个form,第二层循环form得到每一个字段。与forms组件使用一样,需要手动添加form表单和input提交数按钮及csrf_token跨域伪造请求。此外,使用formset,还需要增加 {{ formset.management_form }} , 使用哪个formset就增加哪个formset.management_form.

# multi_add.html
<form method="post">
  {% csrf_token %}
  {{ formset.management_form }}
  <table border="1">
    <thead>
    <tr>
      <th>标题</th>
      <th>URL</th>
      <th>NAME</th>
      <th>菜单</th>
      <th>父权限</th>
    </tr>
    </thead>
    <tbody>
    {% for form in formset %}
      <tr>
        {% for field in form %}
          <td>{{ field }} <span style="color: red;">{{ field.errors.0 }}</span></td>
        {% endfor %}
      </tr>
    {% endfor %}
    </tbody>
  </table>
  <input type="submit" value="提交">
</form>

批量编辑

批量编辑和批量增加大体是一致的,但是存在不同的使用区别。实例化formset对象时默认extra=1,需要手动修改为extra=0;GET请求,页面需要显示默认值,通过参数initial赋值列表内部嵌套字典的数据结构的数据。 且需要传递每行数据的id,告诉formset需要修改的数据id 。此时使用的forms类相比批量添加使用的类多一个id字段, id = forms.IntegerField( widget=forms.HiddenInput()) ,默认隐藏的字段,前端页面不显示。

同理也会遇到唯一约束错误,使用循环和反射为每个字段做数据更新赋值,然后再提交数据库保存。

def multi_edit(request):
  formset_class = formset_factory(MultiUpdatePermissionForm, extra=0)
  if request.method == 'GET':
    formset = formset_class(
      initial=models.Permission.objects.all().values('id', 'title', 'name', 'url', 'menu_id', 'pid_id'))
    return render(request, 'multi_edit.html', {'formset': formset})

  formset = formset_class(data=request.POST)
  if formset.is_valid():
    # 检查formset中没有错误信息,则讲用户提交的数据获取到。
    post_row_list = formset.cleaned_data 
    flag = True
    for i in range(0, formset.total_form_count()):
      row = post_row_list[i]
      if not row:
        continue
      permission_id = row.pop('id')
      try:
        permission_object = models.Permission.objects.filter(id=permission_id).first()
        for key, value in row.items():
          setattr(permission_object, key, value)
        permission_object.validate_unique()
        permission_object.save()

      except Exception as e:
        formset.errors[i].update(e)
        flag = False
    if flag:
      return HttpResponse('提交成功')
    else:
      return render(request, 'multi_edit.html', {'formset': formset})
  return render(request, 'multi_edit.html', {'formset': formset})

前端模板循环显示每个字段时,要判断是否是第一个id字段,如果是第一个就直接 {{field}} ,页面将不会显示。

<form method="post">
  {% csrf_token %}
  {{ formset.management_form }}
  <table border="1">
    <thead>
    <tr>
      <th>标题</th>
      <th>URL</th>
      <th>NAME</th>
      <th>菜单</th>
      <th>父权限</th>
    </tr>
    </thead>
    <tbody>
    {% for form in formset %}
      <tr>
        {% for field in form %}
          {% if forloop.first %}
            {{ field }}
          {% else %}
            <td>{{ field }} <span style="color: red;">{{ field.errors.0 }}</span></td>
          {% endif %}
        {% endfor %}
      </tr>
    {% endfor %}
    </tbody>
  </table>
  <input type="submit" value="提交">
</form>

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
在Python中操作时间之strptime()方法的使用
Dec 30 Python
Python字典操作详细介绍及字典内建方法分享
Jan 04 Python
Python爬虫框架scrapy实现的文件下载功能示例
Aug 04 Python
python中的tcp示例详解
Dec 09 Python
python获取url的返回信息方法
Dec 17 Python
使用OpCode绕过Python沙箱的方法详解
Sep 03 Python
python接口自动化如何封装获取常量的类
Dec 24 Python
jupyter notebook 添加kernel permission denied的操作
Apr 21 Python
解决python图像处理图像赋值后变为白色的问题
Jun 04 Python
python安装读取grib库总结(推荐)
Jun 24 Python
利用python3筛选excel中特定的行(行值满足某个条件/行值属于某个集合)
Sep 04 Python
Python 使用xlwt模块将多行多列数据循环写入excel文档的操作
Nov 10 Python
Python手绘可视化工具cutecharts使用实例
Dec 05 #Python
Python实现变声器功能(萝莉音御姐音)
Dec 05 #Python
关于numpy数组轴的使用详解
Dec 05 #Python
python 字典访问的三种方法小结
Dec 05 #Python
python 实现dict转json并保存文件
Dec 05 #Python
numpy 声明空数组详解
Dec 05 #Python
Numpy将二维数组添加到空数组的实现
Dec 05 #Python
You might like
PHP 杂谈《重构-改善既有代码的设计》之四 简化条件表达式
2012/04/09 PHP
PHP stripos()函数及注意事项的分析
2013/06/08 PHP
解析php dirname()与__FILE__常量的应用
2013/06/24 PHP
php通过隐藏表单控件获取到前两个页面的url
2014/09/09 PHP
symfony表单与页面实现技巧
2015/01/26 PHP
php输出指定时间以前时间格式的方法
2015/03/21 PHP
JavaScript数组各种常见用法实例分析
2015/08/04 Javascript
AngularJs学习第五篇从Controller控制器谈谈$scope作用域
2016/06/08 Javascript
javascript 利用arguments实现可变长参数
2016/11/21 Javascript
JavaScript 字符串常用操作小结(非常实用)
2016/11/30 Javascript
Bootstrap CSS布局之列表
2016/12/15 Javascript
javascript常用经典算法详解
2017/01/11 Javascript
基于js粘贴事件paste简单解析以及遇到的坑
2017/09/07 Javascript
在vue项目中,使用axios跨域处理
2018/03/07 Javascript
详解vue-cli官方脚手架配置
2018/07/20 Javascript
新年快乐! javascript实现超级炫酷的3D烟花特效
2019/01/30 Javascript
Vue通过WebSocket建立长连接的实现代码
2019/11/05 Javascript
在vue和element-ui的table中实现分页复选功能
2019/12/04 Javascript
微信小程序学习总结(一)项目创建与目录结构分析
2020/06/04 Javascript
JS检测浏览器开发者工具是否打开的方法详解
2020/10/02 Javascript
python网络编程学习笔记(八):XML生成与解析(DOM、ElementTree)
2014/06/09 Python
简单理解Python中基于生成器的状态机
2015/04/13 Python
Python 字符串大小写转换的简单实例
2017/01/21 Python
Python排序搜索基本算法之选择排序实例分析
2017/12/09 Python
Python创建一个空的dataframe,并循环赋值的方法
2018/11/08 Python
Python实现Restful API的例子
2019/08/31 Python
解析PyCharm Python运行权限问题
2020/01/08 Python
MoviePy简介及Python视频剪辑自动化
2020/12/18 Python
美国诺德斯特龙百货官网:Nordstrom
2016/08/23 全球购物
将世界上最美丽的摄影作品转化为艺术作品:Photos.com
2017/11/28 全球购物
运动会广播稿200米
2014/01/27 职场文书
求职简历的自我评价
2014/01/31 职场文书
护士自荐信怎么写
2015/03/06 职场文书
2016年教师反腐倡廉心得体会
2016/01/13 职场文书
十个Python自动化常用操作,即拿即用
2021/05/10 Python
原生JS实现分页
2022/04/19 Javascript