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中用于计算对数的log()方法
May 15 Python
在Django的URLconf中使用命名组的方法
Jul 18 Python
python导出chrome书签到markdown文件的实例代码
Dec 27 Python
Python八大常见排序算法定义、实现及时间消耗效率分析
Apr 27 Python
Python数据类型之List列表实例详解
May 08 Python
如何用Python做一个微信机器人自动拉群
Jul 03 Python
一行Python代码制作动态二维码的实现
Sep 09 Python
Python计算两个矩形重合面积代码实例
Sep 16 Python
Python 获取项目根路径的代码
Sep 27 Python
Pytorch 搭建分类回归神经网络并用GPU进行加速的例子
Jan 09 Python
Python下利用BeautifulSoup解析HTML的实现
Jan 17 Python
Python检测端口IP字符串是否合法
Jun 05 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 Mssql操作简单封装支持存储过程
2009/12/11 PHP
php cli 方式 在crotab中运行解决
2010/02/08 PHP
PHP随机数生成代码与使用实例分析
2011/04/08 PHP
PHP人民币金额数字转中文大写的函数代码
2013/02/27 PHP
php读取图片内容并输出到浏览器的实现代码
2013/08/08 PHP
php另类上传图片的方法(PHP用Socket上传图片)
2013/10/30 PHP
解读PHP的Yii框架中请求与响应的处理流程
2016/03/17 PHP
使用js对select动态添加和删除OPTION示例代码
2013/08/12 Javascript
Jquery弹出层插件ThickBox的使用方法
2014/12/09 Javascript
jquery实现鼠标点击后展开列表内容的导航栏效果
2015/09/14 Javascript
基于jQuery实现点击弹出层实例代码
2016/01/01 Javascript
Nodejs如何搭建Web服务器
2016/03/28 NodeJs
基于Javascript倒计时效果
2016/12/22 Javascript
javascript ES6 新增了let命令使用介绍
2017/07/07 Javascript
微信小程序中post方法与get方法的封装
2017/09/26 Javascript
JS监听事件的叠加和移除功能
2018/11/19 Javascript
js设计模式之单例模式原理与用法详解
2019/08/15 Javascript
jQuery操作事件完整实例分析
2020/01/10 jQuery
[20:39]DOTA2-DPC中国联赛 正赛开幕式 1月18日
2021/03/11 DOTA
python中__call__内置函数用法实例
2015/06/04 Python
Pycharm之快速定位到某行快捷键的方法
2019/01/20 Python
详解Python_shutil模块
2019/03/15 Python
将Python字符串生成PDF的实例代码详解
2019/05/17 Python
Django 权限认证(根据不同的用户,设置不同的显示和访问权限)
2019/07/24 Python
windows下Python安装、使用教程和Notepad++的使用教程
2019/10/06 Python
python对象销毁实例(垃圾回收)
2020/01/16 Python
使用Python求解带约束的最优化问题详解
2020/02/11 Python
python 成功引入包但无法正常调用的解决
2020/03/09 Python
亚马逊印度站:Amazon.in
2017/10/15 全球购物
介绍一下Java中的Class类
2015/04/10 面试题
军校制空专业毕业生自我鉴定
2013/11/16 职场文书
便利店的创业计划书
2014/01/15 职场文书
公司的门卫岗位职责
2014/09/09 职场文书
2014年后勤管理工作总结
2014/12/01 职场文书
三峡导游词
2015/01/31 职场文书
安全教育第一课观后感
2015/06/17 职场文书