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之深入变量和引用对象
Sep 24 Python
用Python的Django框架完成视频处理任务的教程
Apr 02 Python
CentOS6.5设置Django开发环境
Oct 13 Python
Python使用getpass库读取密码的示例
Oct 10 Python
Python文本特征抽取与向量化算法学习
Dec 22 Python
python批量创建指定名称的文件夹
Mar 21 Python
Python常用模块之requests模块用法分析
May 15 Python
numpy concatenate数组拼接方法示例介绍
May 27 Python
kali中python版本的切换方法
Jul 11 Python
详解python中__name__的意义以及作用
Aug 07 Python
Python 实现PS滤镜的旋涡特效
Dec 03 Python
用python获取txt文件中关键字的数量
Dec 24 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实现的获取网站备案信息查询代码(360)
2013/09/23 PHP
Laravel 5框架学习之表单验证
2015/04/08 PHP
php页面,mysql数据库转utf-8乱码,utf-8编码问题总结
2015/08/27 PHP
Zend Framework实现Zend_View集成Smarty模板系统的方法
2016/03/05 PHP
thinkPHP5.0框架URL访问方法详解
2017/03/18 PHP
Mac系统完美安装PHP7详细教程
2017/06/06 PHP
php+redis实现商城秒杀功能
2020/11/19 PHP
PHP的静态方法与普通方法用法实例分析
2019/09/26 PHP
PHP设计模式(三)建造者模式Builder实例详解【创建型】
2020/05/02 PHP
Firebug 字幕文件JSON地址获取代码
2009/10/28 Javascript
JS子父窗口互相操作取值赋值的方法介绍
2013/05/11 Javascript
浅析JavaScript基本类型与引用类型
2014/05/28 Javascript
JavaScript中实现Map的示例代码
2015/09/09 Javascript
jquery UI Datepicker时间控件的使用方法(终结版)
2015/11/07 Javascript
JSON字符串转换JSONObject和JSONArray的方法
2016/06/03 Javascript
json对象与数组以及转换成js对象的简单实现方法
2016/06/24 Javascript
js 获取当前web应用的上下文路径实现方法
2016/08/19 Javascript
WebPack配置vue多页面的技巧
2018/05/15 Javascript
详解JavaScript原生封装ajax请求和Jquery中的ajax请求
2019/02/14 jQuery
vue路由导航守卫和请求拦截以及基于node的token认证的方法
2019/04/07 Javascript
在layui中对table中的数据进行判断(0、1)转换为提示信息的方法
2019/09/28 Javascript
AI小程序之语音听写来了,十分钟掌握百度大脑语音听写全攻略
2020/03/13 Javascript
Vue 防止短时间内连续点击后多次触发请求的操作
2020/11/11 Javascript
[49:20]VG vs TNC Supermajor小组赛B组败者组决赛 BO3 第二场 6.2
2018/06/03 DOTA
使用python编写脚本获取手机当前应用apk的信息
2014/07/21 Python
python tkinter窗口最大化的实现
2019/07/15 Python
python自动生成model文件过程详解
2019/11/02 Python
PyQt5多线程防卡死和多窗口用法的实现
2020/09/15 Python
Django扫码抽奖平台的配置过程详解
2021/01/14 Python
英国山地公路自行车商店:Tweeks Cycles
2018/03/16 全球购物
weblogic面试题
2016/03/07 面试题
怎样从/向数据文件读/写结构
2014/11/23 面试题
新闻编辑自荐信
2013/11/03 职场文书
群众路线教育实践活动整改方案(个人版)
2014/10/25 职场文书
CSS3 制作的悬停缩放特效
2021/04/13 HTML / CSS
vue完美实现el-table列宽自适应
2021/05/08 Vue.js