• form组件之formset实现批量添加和编辑


    示例效果:

     

     form组件或ModelForm组件用于做一个表单验证,formset是用于做多个表单验证的组件。

    formset/settings.py

    LANGUAGE_CODE = 'zh-hans'
    # 把报错信息改为中文

    formset/urls.py

    from django.contrib import admin
    from django.urls import path, re_path
    from app01 import views
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        re_path(r'^multi_add/$', views.multi_add, name="multi_add"),
        re_path(r'^multi_edit/$', views.multi_edit, name="multi_edit"),
        re_path(r'', views.multi_add),
    ]

    app01/models.py

    from django.db import models
    
    # Create your models here.
    
    
    class Menu(models.Model):
        """一级菜单表"""
        title = models.CharField(
            verbose_name="一级菜单名称",
            max_length=32,
        )
        icon = models.CharField(
            verbose_name="图标",
            max_length=64,
            # null=True,  # 表示数据库中可以为空
            # blank=True,  # admin后台管理中可以输入为空
        )
    
        def __str__(self):
            return self.title
    
    
    class Permission(models.Model):
        """权限表"""
        title = models.CharField(
            verbose_name="权限名称",
            max_length=32,
        )
        url = models.CharField(
            verbose_name="含正则URL",
            max_length=128,
        )
        name = models.CharField(
            verbose_name="URL别名",
            max_length=32,
            unique=True,
        )
        menu = models.ForeignKey(
            to="Menu",
            on_delete=models.CASCADE,
            verbose_name="所属二级菜单",
            null=True,  # 并不是所有的url都可以做二级菜单,所以这里需要设置null=True
            blank=True,
            help_text="null表示不是二级菜单,非null表示是二级菜单",
        )
        pid = models.ForeignKey(
            verbose_name="关联的权限",
            to="Permission",
            null=True,  # 如果是二级菜单,那么此字段为空,非二级菜单此字段不为空
            blank=True,
            on_delete=models.CASCADE,
            help_text="对于非菜单权限需要选择一个可以成为菜单的权限,用于做默认展开和选中菜单",
            related_name="parents",
        )
    
        def __str__(self):
            return self.title

    app01/views.py

    from django.shortcuts import render, HttpResponse
    from django import forms
    from app01 import models
    from django.forms import formset_factory
    # Create your views here.
    
    
    class MultiPermissionForm(forms.Form):
        title = forms.CharField(
            widget=forms.TextInput(attrs={"class": "form-control"})
        )
        url = forms.CharField(
            widget=forms.TextInput(attrs={"class": "form-control"})
        )
        name = forms.CharField(
            widget=forms.TextInput(attrs={"class": "form-control"})
        )
        menu_id = forms.ChoiceField(
            choices=[(None, "-----")],
            widget=forms.Select(attrs={"class": "form-control"}),
            required=False,
        )
        pid_id = forms.ChoiceField(
            choices=[(None, "-----")],
            widget=forms.Select(attrs={"class": "form-control"}),
            required=False,
        )
    
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
            self.fields["menu_id"].choices += models.Menu.objects.values_list("id", "title")
            self.fields["pid_id"].choices += models.Permission.objects.filter(
                pid__isnull=True).exclude(menu__isnull=True).values_list("id", "title")
    
    
    class MultiUpdatePermissionForm(forms.Form):
        """多加一个id,这样方便于编辑的时候对相应的表单进行更新操作"""
        id = forms.IntegerField(
            widget=forms.HiddenInput(),  # 隐藏的输入框,无需给用户知道
        )
        title = forms.CharField(
            widget=forms.TextInput(attrs={"class": "form-control"})
        )
        url = forms.CharField(
            widget=forms.TextInput(attrs={"class": "form-control"})
        )
        name = forms.CharField(
            widget=forms.TextInput(attrs={"class": "form-control"})
        )
        menu_id = forms.ChoiceField(
            choices=[(None, "-----")],
            widget=forms.Select(attrs={"class": "form-control"}),
            required=False,
        )
        pid_id = forms.ChoiceField(
            choices=[(None, "-----")],
            widget=forms.Select(attrs={"class": "form-control"}),
            required=False,
        )
    
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
            self.fields["menu_id"].choices += models.Menu.objects.values_list("id", "title")
            self.fields["pid_id"].choices += models.Permission.objects.filter(
                pid__isnull=True).exclude(menu__isnull=True).values_list("id", "title")
    
    
    def multi_add(request):
        """
        批量添加
        :param request:
        :return:
        """
        # extra代表实例化了2个MultiPermissionForm
        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)
        # [form(字段, 错误), form(字段, 错误), form(字段, 错误)]
        # 如果表单数据都为空,那么不会做任何校验
        if formset.is_valid():
            # print(formset.cleaned_data)  # [{}, {}]
            flag = True
            # 将unique的错误添加到指定字段
            post_row_list = formset.cleaned_data  # 检查formset中有没有错误信息,将用户提交的数据获取到
            """
            formset.cleaned_data = [
                    {'title': '123', 'url': '123', 'name': '123', 'menu_id': '1', 'pid_id': ''},
                    {'title': '321', 'url': '321', 'name': '321', 'menu_id': '2', 'pid_id': ''}
                ]
            """
            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})
            # for row in formset.cleaned_data:
            #     # [ {}, {}, {}]
            #     # models.Permission.objects.create(**row)  # 这种方式不推荐,如果是unique错误此种方式捕捉不到
            #     try:
            #         obj = models.Permission(**row)
            #         obj.validate_unique()  # 检查当前对象在数据库是否存在唯一的异常
            #         obj.save()
            #     except Exception as e:
            #         pass
    
        return render(request, "multi_add.html", {"formset": formset})
    
    
    def multi_edit(request):
        """
        批量编辑
        :param request:
        :return:
        """
        formset_class = formset_factory(MultiUpdatePermissionForm, extra=0)
        if request.method == "GET":
            # formset = formset_class(
            #     initial=[
            #         {"id": 1, "title": "x1", "url": "xxx"},
            #         {"id": 2, "title": "x2", "url": "ooo"}
            #     ]
            # )
            formset = formset_class(
                initial=models.Permission.objects.all().values("id", "name", "title", "url", "menu_id", "pid_id")
            )
            return render(request, "multi_edit.html", {"formset": formset})
        formset = formset_class(data=request.POST)
        if formset.is_valid():
            flag = True
            post_row_list = formset.cleaned_data
            for i in range(0, formset.total_form_count()):
                row = post_row_list[i]
                if not row:
                    continue
                permission_id = row.pop("id")  # 拿到表单的id
                # models.Permission.objects.filter(id=permission_id).update(**row)  # 和上面的一样,unique错误无法捕捉
                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)
            if flag:
                return HttpResponse("提交成功")
            else:
                return render(request, "multi_edit.html", {"formset": formset})
        return render(request, "multi_edit.html", {"formset": formset})

    templates/multi_add.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <h1>批量添加</h1>
    <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>
        <p><input type="submit" value="提交"></p>
        <a href="{% url 'multi_edit' %}">批量编辑</a>
    </form>
    </body>
    </html>

    templates/multi_edit.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <h1>批量编辑</h1>
    <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>
        <p><input type="submit" value="提交"></p>
        <a href="{% url 'multi_add' %}">批量添加</a>
    </form>
    </body>
    </html>

     

  • 相关阅读:
    java设计模式笔记(1)-适配器模式
    linux下源码编译安装mysql
    spring boot入门
    2016年终总结
    CentOS获取公网IP
    shell中的$0 $n $# $* $@ $? $$
    shell中各种括号的作用详解()、(())、[]、[[]]、{}
    10个实战及面试常用Linux Shell脚本编写
    记录centos下nl与cat -n的不同
    grep、cut、awk、sed的使用
  • 原文地址:https://www.cnblogs.com/xuewei95/p/15885450.html
Copyright © 2020-2023  润新知