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 相关文章推荐
在Windows8上的搭建Python和Django环境
Jul 03 Python
python距离测量的方法
Mar 06 Python
python批量导入数据进Elasticsearch的实例
May 30 Python
Python 中的lambda函数介绍
Oct 10 Python
对python 操作solr索引数据的实例详解
Dec 07 Python
浅谈Pandas Series 和 Numpy array中的相同点
Jun 28 Python
python selenium 查找隐藏元素 自动播放视频功能
Jul 24 Python
python输出带颜色字体实例方法
Sep 01 Python
python根据时间获取周数代码实例
Sep 30 Python
python GUI库图形界面开发之PyQt5简单绘图板实例与代码分析
Mar 08 Python
解决python对齐错误的方法
Jul 16 Python
python之pygame模块实现飞机大战完整代码
Nov 29 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
codeigniter自带数据库类使用方法说明
2014/03/25 PHP
PHP递归实现快速排序的方法示例
2017/12/18 PHP
PHP 获取客户端 IP 地址的方法实例代码
2018/11/11 PHP
strpos() 函数判断字符串中是否包含某字符串的方法
2019/01/16 PHP
漂亮的jquery提示效果(仿腾讯弹出层)
2013/02/05 Javascript
javascript实现图片轮播效果
2016/01/20 Javascript
Node.js编写爬虫的基本思路及抓取百度图片的实例分享
2016/03/12 Javascript
jQuery简单动画变换效果实例分析
2016/07/04 Javascript
使用ReactJS实现tab页切换、菜单栏切换、手风琴切换和进度条效果
2016/10/17 Javascript
JScript实现地址选择功能
2017/08/15 Javascript
js中let和var定义变量的区别
2018/02/08 Javascript
node.js中TCP Socket多进程间的消息推送示例详解
2018/07/10 Javascript
Element UI 自定义正则表达式验证方法
2018/09/04 Javascript
js中对象和面向对象与Json介绍
2019/01/21 Javascript
vue+element 模态框表格形式的可编辑表单实现
2019/06/07 Javascript
详解微信小程序之提高应用速度小技巧
2020/01/07 Javascript
[01:58]DOTA2上海特级锦标赛现场采访:RTZ这个ID到底好不好
2016/03/25 DOTA
[05:49]DOTA2-DPC中国联赛 正赛 Elephant vs LBZS 选手采访
2021/03/11 DOTA
python 实现删除文件或文件夹实例详解
2016/12/04 Python
python探索之BaseHTTPServer-实现Web服务器介绍
2017/10/28 Python
python实现人脸识别经典算法(一) 特征脸法
2018/03/13 Python
PyQt5重写QComboBox的鼠标点击事件方法
2019/06/25 Python
python异步编程 使用yield from过程解析
2019/09/25 Python
关于pandas的离散化,面元划分详解
2019/11/22 Python
python shapely.geometry.polygon任意两个四边形的IOU计算实例
2020/04/12 Python
TensorFlow Autodiff自动微分详解
2020/07/06 Python
Nike西班牙官方网站:Nike.com (ES)
2017/10/30 全球购物
Europcar西班牙:全球汽车租赁领域的领导者
2018/09/17 全球购物
美国爆米花工厂:The Popcorn Factory
2019/09/14 全球购物
实验教师岗位职责
2014/02/13 职场文书
小学教学随笔感言
2014/02/26 职场文书
毕业生简历自我评价范文
2014/04/09 职场文书
处罚决定书范文
2015/06/24 职场文书
《夜莺的歌声》教学反思
2016/02/22 职场文书
2019年英语版感谢信(8篇)
2019/09/29 职场文书
小学四年级作文之最感动的一件事
2019/11/01 职场文书