什么是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>

django-formset实现数据表的批量操作的更多相关文章

  1. django+uwsgi+nginx数据表过大引起"out of memory for query result"

    昨天负责的一个项目突然爆“out of memory for query result”. 背景 项目的数据表是保存超过10m的文本数据,通过json方式保存进postgres中,上传一个13m的大文 ...

  2. Django创建mysql数据表流程

    在Django项目建好后,在setting.py中设置好mysql连接参数: DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysq ...

  3. Django Model 数据表

    Django Model 定义语法 版本:1.7主要来源:https://docs.djangoproject.com/en/1.7/topics/db/models/ 简单用法 from djang ...

  4. python Django教程 之 模型(数据库)、自定义Field、数据表更改、QuerySet API

    python  Django教程  之 模型(数据库).自定义Field.数据表更改.QuerySet API 一.Django 模型(数据库) Django 模型是与数据库相关的,与数据库相关的代码 ...

  5. Django之路:模型(数据库)和自定义Field以及数据表的更改

    一.Django 模型(数据库) Django模型是与数据库相关的,与数据库相关的代码一般写在models.py中,Django支持sqlite3,MySQL,PostgreSQL等数据库,只需要在s ...

  6. Django学习手册 - admin后台 切换成中文显示/添加数据表

    Django admin后台管理 切换成中文界面: 站点显示为中文: 在setting 里面修改 LANGUAGE_CORE = 'zh-Hans' 字段名显示中文 class Test(models ...

  7. Django 数据表更改

    Django 数据表更改 « Django 开发内容管理系统(第四天) Django 后台 » 我们设计数据库的时候,早期设计完后,后期会发现不完善,要对数据表进行更改,这时候就要用到本节的知识. D ...

  8. 第三百七十七节,Django+Xadmin打造上线标准的在线教育平台—apps目录建立,以及数据表生成

    第三百七十七节,Django+Xadmin打造上线标准的在线教育平台—apps目录建立,以及数据表生成 apps目录建立 我们创建一个apps目录,将所有的app放到apps目录里去,这样方便管理,也 ...

  9. Django数据库数据表操作

    建立表单 django通过设置类来快速建表,打开models.py 例: from __future__ import unicode_literals from django.db import m ...

随机推荐

  1. TensorFlow深度学习基础与应用实战高清视频教程

    TensorFlow深度学习基础与应用实战高清视频教程,适合Python C++ C#视觉应用开发者,基于TensorFlow深度学习框架,讲解TensorFlow基础.图像分类.目标检测训练与测试以 ...

  2. (JavaScript) 时间转为几天前、几小时前、几分钟前

    // 时间戳转多少分钟之前 getDateDiff(dateTimeStamp) { // 时间字符串转时间戳 var timestamp = new Date(dateTimeStamp).getT ...

  3. 使用Windows Powershell卸载和安装Win10 原生应用的方法

    新装的Win10带有大量的实际工作和生活中不怎么常用的APP,如果觉得这些APP占用磁盘空间或者想要卸载这些应用(APP),Win10下并不能使用Windows 应用管理器直接图形化地卸载这些应用,而 ...

  4. 学习笔记47_关于Session局限性问题,Memcache

    三大问题: 1.Session性能问题 2.不能稳定输出.考虑使用进程外Session 3.组成集群,登录数据进行共享 (比如说像百度,百度网盘,百度文库等是使用不同的服务机器的,怎样避免使用的时候不 ...

  5. iSCSI 共享存储

         iSCSI(Internet Small Computer System Interface,发音为/ˈаɪskʌzi/),Internet小型计算机系统接口,又称为IP-SAN,是一种基于 ...

  6. [考试反思]1106csp-s模拟测试103: 渺茫

    7点之前上不了博客,用gedit写的.谅解一下. 看起来不是特别惨?但是被sdfz爆踩了...而且其实并不能说“不是特别惨”吧90分算个啥啊?还凑不出个T2的AC难易度评估错误,T2是最简单的没看出来 ...

  7. [考试反思]1029csp-s模拟测试92:弱智

    我只能这么评价我自己. 看这个提交时间...我没话可说... T1半个世界都A了还是切不掉.又一次挂细节. T2不会证明的乱搞(虽然可以证明)A了没什么可说的算是水过. T3之前水过的题(打的次正解) ...

  8. CSPS模拟 87

    考场上思考量不可减少 否则分数秒变弟弟,考后秒变弱智 T1 二分答案.打的稍恶心 T2 线段树维护“如果我在这个点开枪,前方点的贡献有多大” 想明白了就很好理解了 另外已经飞过去八千里的鸟还输入进来干 ...

  9. st表复习笔记

    st表,一种高效的区间最值查询(RMQ)算法.本质其实是一个动态规划. 其实吧,对于看过线性dp的人来说应该不难理解,只是处理有些麻烦.但是本土狗因为-1的问题居然改了许久... 用两个2^i的区间把 ...

  10. 转载]OK6410之tftp下载内核,nfs挂载文件系统全过程详解[转]

    原文地址:OK6410之tftp下载内核,nfs挂载文件系统全过程详解[转]作者:千山我独行 由于工作的平台也是嵌入式,差不多的平台,所以一直就没有把自己买过来的ok6410板子好好玩玩.以前一直都是 ...