formset进行批量操作

项目的目录结构

效果如下

添加界面

编辑界面

models中生成数据库表的类

定义了两张表:按钮表与权限表:

from django.db import models

class Menu(models.Model):
    """
    菜单表
    """
    title = models.CharField(verbose_name='菜单名称', max_length=32)
    icon = models.CharField(verbose_name='图标', max_length=32)

    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(verbose_name='所属菜单', to='Menu', null=True, blank=True, help_text='null表示不是菜单;非null表示是二级菜单') pid = models.ForeignKey(verbose_name='关联的权限', to='Permission', null=True, blank=True, related_name='parents', help_text='对于非菜单权限需要选择一个可以成为菜单的权限,用户做默认展开和选中菜单') def __str__(self): return self.title

forms中用于校验的类

用于校验的类我写在了app01应用下forms.py文件中了~

forms中的校验类的写法跟form表单进行校验时的写法一模一样!

但是需要注意“菜单”与“父权限”是需要用下拉框checkbox多选一的~需要在初始化方法(__init__方法)中定制一下~

注意:编辑比删除要多一个id字段~~在做更新的时候,是按照id作为条件的!

# -*- coding:utf-8 -*-
from django import forms

from app01 import models

# 添加
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')

视图函数的写法——注意添加与编辑的逻辑-注释!

视图函数的写法固定~~注意get请求与post请求不一样~post请求需要参数~

添加与编辑中“唯一性”的判断;编辑用反射setattr方法!

from django.forms import formset_factory
from django.shortcuts import render, HttpResponse

from app01 import models
from app01.forms import MultiPermissionForm,MultiUpdatePermissionForm


# 批量添加
def multi_add(request):
    # 1~利用前面定义的“Form子类”得到一个forset类
    formset_class = formset_factory(MultiPermissionForm, extra=2)
    # get请求~
    if request.method == 'GET':
        # 2~实例化一个formset队形~不加参数
        formset = formset_class()
        return render(request, 'multi_add.html', {'formset': formset})

    # 后台用post方法提交了数据的话~
    #~~用提交过来的数据作为参数
    formset = formset_class(data=request.POST)
    if formset.is_valid():
        flag = True

        # 往数据库中添加数据~~用这种方法!~需要捕获一个“联合唯一”的异常~
        # 重要的小细节:先把cleaned_data传入一个列表~因为errors与cleaned_data是互斥的!!
        # 检查formset中没有错误信息,则讲用户提交的数据获取到。
        post_row_list = formset.cleaned_data
        # “总共有多少个form”~将错误信息放在对应的位置,需要这个索引i的值!
        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})


# 批量编辑
def multi_edit(request):
    # 1 得到一个formset的类
    formset_class = formset_factory(MultiUpdatePermissionForm, extra=0)
    if request.method == 'GET':
        # 2 用这个类实例化一个formset对象~注意参数的写法
        formset = formset_class(
            # 将数据库中的数据传给initial~页面可以显示原有的数据!
            initial=models.Permission.objects.all().values('id', 'title', 'name', 'url', 'menu_id', 'pid_id'))
        return render(request, 'multi_edit.html', {'formset': formset})

    # post方法提交的话~
    #~~用提交过来的数据作为参数
    formset = formset_class(data=request.POST)
    if formset.is_valid():
        post_row_list = formset.cleaned_data  # 检查formset中没有错误信息,则讲用户提交的数据获取到。
        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对象去做校验~
                permission_object = models.Permission.objects.filter(id=permission_id).first()
                #  *** 通过“反射”来做!
                for key, value in row.items():
                    # *** setattr~ 属性的名字是一样的~
                    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})

模板渲染

模板渲染需要注意3点:

1、用form表单~post方式提交

2、form表单中不仅要加csrf_token,还要加formset的一个变量:{{ formset.management_form }}

3、遍历后台传过来的formset对象的时候,注意是两层:第一次取出form对象,第二次用这个form对象生成table中的一行数据。

添加的模板

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form method="post">
    {# 记得写csrf_token #}
    {% csrf_token %}
    {# 用formn_set记得前端页面必须写这个 #}
    {{ formset.management_form }}

    <table border="1">
        <thead>
        <tr>
            <th>标题</th>
            <th>URL</th>
            <th>NAME</th>
            <th>菜单</th>
            <th>父权限</th>
        </tr>
        </thead>
        <tbody>
        {# 两层循环~~外层循环的是formset #}
        {% for form in formset %}
            <tr>
                {# 内层循环的是form #}
                {% 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>
</body>
</html>

编辑的模板

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form method="post">
    {# 前端既要写csrftoken 也要写formset的设置! #}
    {% 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>
        {# 两层循环-外层遍历formset~第二层遍历form #}
        {% 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>
</body>
</html>

~~~

posted on 2019-06-25 20:23  江湖乄夜雨  阅读(239)  评论(0编辑  收藏  举报