什么是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. presto-mysql/elasticsearch6.0.0安装部署测试,异种数据源关联查询入门实践

    本文简单记录一次实践使用过程,涉及presto-mysql,presto-elasticsearch,文中参数未做注释,请参考官方文档,希望能帮到大家 1 下载安装 presto-0.228 < ...

  2. winform 数据库资料导出Excel方法(适用于资料数据较多加载慢,不用呈现至DatagridView)

    Private Sub savefile(ByVal dgv2 As DataTable) Dim app As Object = CreateObject("Excel.Applicati ...

  3. 在虚拟机上的关于NFS网络文件系统

    小知识: NFS(Network Files System)即网络文件系统,NFS文件系统协议允许网络中的主机通过TCP/IP协议进行资源共享,NFS客户端可以像使用本地资源一样读写远端NFS服务端的 ...

  4. CVE-2019-13272Linuxkernel权限许可和访问控制问题漏洞

    漏洞简介: Linuxkernel是美国Linux基金会发布的开源操作系统Linux所使用的内核. Linuxkernel5.1.17之前版本中存在安全漏洞,该漏洞源于kernel/ptrace.c文 ...

  5. 命令行代理神器 proxychains

    因为某些原因,我们需要在命令行下载一些国外的资源,这个时候如果使用 wget,curl,或者 aria2c 的时候,往往又没有速度.这个时候我们需要使用代理来进行加速. 我本地搭的有 ss,但 ss ...

  6. raw文件转mha文件

    raw格式 在体数据(volume)中,经常会遇到raw文件,raw文件就是其实就是所有体素组成的文件,raw文件必须还有一些描信息才能用(因为得知道数据的size,type,spacing等),就像 ...

  7. C语言程序设计100例之(4):水仙花数

    例4    水仙花数 题目描述 一个三位整数(100-999),若各位数的立方和等于该数自身,则称其为“水仙花数”(如:153=13+53+33),找出所有的这种数. 输入格式 没有输入 输出格式 若 ...

  8. golang 服务诡异499、504网络故障排查

    事故经过 排查 总结 事故经过 11-01 12:00 中午午饭期间,手机突然收到业务网关非200异常报警,平时也会有一些少量499或者网络抖动问题触发报警,但是很快就会恢复(目前配置的报警阈值是5% ...

  9. 使用JSP脚本在页面输出九九乘法表

    <% int i,j; for(i=1;i<10;i++) { for(j=1;j<=i;j++) { out.println(i+"*"+j+"=&q ...

  10. Elasticsearch系列---分布式架构机制讲解

    概要 本篇主要介绍Elasticsearch的数据索引时的分片机制,集群发现机制,primary shard与replica shard是如何分工合作的,如何对集群扩容,以及集群的容错机制. 分片机制 ...