什么是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. RocketMQ4.2 最佳实践之集群搭建

    学习了RocketMQ的基本概念后,我们来看看RocketMQ最简单的使用场景.RocketMQ的服务器最简单的结构,必须包含一个NameServer和一个Broker.Producer把某个主题的消 ...

  2. Mysql数据一般问题

    数据插入中文全部变为???问题: 1.停止Mysql服务: 2.修改C:\Program Files (x86)\MySQL\MySQL Server 5.5\My.ini default-chara ...

  3. android内嵌H5页(webview)如何定位

    一.切换至webview后再定位元素 (1)获取页面上下文 contexts = driver.contexts (2)切换至webview driver.switch_to.context(cont ...

  4. nginx篇最初级用法之三种虚拟主机基于域名\基于端口\基于IP地址端口的虚拟主机

    在nginx中虚拟主机的类型与apache一样也有三种 1.基于域名的虚拟主机 2.基于端口的虚拟主机 3.基于IP地址端口的虚拟主机 在nginx配置文件中每一个server为一个虚拟主机如果需要多 ...

  5. 一文学会Go语言

    go语言随手记 -- go语言定位于高并发服务端程序 1.基本数据类型 boolstringint int8 int16 int32 int64uint uint8 uint16 uint32 uin ...

  6. 大数据之路day04_2--经典bug(equals与==比较不同,break的跳出不同)

    一.equals与==比较不同 在实现某个人去5个商场去购物,控制台输入是否购物(Y/N)的时候,在比较出了问题,发现无论输入什么都是false,后来查阅资料发现,字符串的比较,==和equals不一 ...

  7. linux常用的命令解释

    1.man命令的操作按键: 按键 用处 空格键 向下翻一页. [Page Down] 向下翻一页. [Page Up] 向上翻一页. [HOME] 直接前往首页. [END] 直接前往尾页. /关键词 ...

  8. 每天一道算法题-leetcode136-只出现一次的数字

    前言 打卡第一天 2019.10.26日打卡 算法,即解决问题的方法.同一个问题,使用不同的算法,虽然得到的结果相同,但是耗费的时间和资源是不同的.这就需要我们学习算法,找出哪个算法更好. 大家都知道 ...

  9. nyoj 277-车牌号 (map, pair, iterator)

    277-车牌号 内存限制:64MB 时间限制:3000ms 特判: No 通过数:9 提交数:13 难度:1 题目描述: 茵茵很喜欢研究车牌号码,从车牌号码上可以看出号码注册的早晚,据研究发现,车牌号 ...

  10. RHEL7.2 安装Hadoop-2.8.2

    创建三台虚拟机,IP地址为:192.168.169.101,192.168.169.102,192.168.169.103 将192.168.169.102为namenode,192.168.169. ...