权限组件(11):基于formset实现批量增加

效果图:

增加页面:

编辑页面:

 

因为后面要对权限进行批量操作,所以先用这个示例演示下如何实现批量操作

数据库

 

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表示是二级菜单', on_delete=models.CASCADE)

    pid = models.ForeignKey(verbose_name='关联的权限', to='Permission', null=True, blank=True, related_name='parents',
                            help_text='对于非菜单权限需要选择一个可以成为菜单的权限,用于做默认展开和选中菜单',
                            on_delete=models.CASCADE)

    def __str__(self):
        return self.title

 

 

一、配置路由

from django.urls import path
from formset import views

urlpatterns = [
    path('multi/add', views.multi_add),
    path('multi/edit', views.multi_edit),
]

二、forms表单验证

from django import forms

from formset import models


class MultiPermissionForm(forms.Form):
    title = forms.CharField()
    url = forms.CharField()
    name = forms.CharField()
    menu_id = forms.ChoiceField(
        choices=[(None, '----------')],
        required=False
    )
    pid_id = forms.ChoiceField(
        choices=[(None, '----------')],
        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 = forms.IntegerField(
        widget=forms.HiddenInput()
    )
    title = forms.CharField()
    url = forms.CharField()
    name = forms.CharField()
    menu_id = forms.ChoiceField(
        choices=[(None, '----------')],
        required=False
    )
    pid_id = forms.ChoiceField(
        choices=[(None, '----------')],
        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')

三、视图函数

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

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


def multi_add(request):
    """
    批量增加
    :param request:
    :return:
    """

    formset_class = formset_factory(MultiPermissionForm, extra=5)  # 在内部生成五个form表单

    if request.method == 'GET':
        formset = formset_class()
        return render(request, 'multi_add.html', {'formset': formset})

    formset = formset_class(data=request.POST)  # 储存的所有信息,包括html标签
    if formset.is_valid():
        no_repeat_field = True

        # 要把cleaned_data放到for循环上面,因为在下面一旦cleaned_data检测到错误信息就会报错。里面储存了一个个form[{},{},{}......]
        form_list = formset.cleaned_data  # 检查formset中有没有错误信息,没有则将用户提交的数据取到。有错误信息就报错
        for num in range(0, formset.total_form_count()):
            form = form_list[num]  # 一个具体的form
            if not form:
                continue
            try:
                # 下面的方式和model.Permission.object.create(**row)效果一样,这里用这种方式是为了捕获唯一性错误
                permission_obj = models.Permission(**form)
                permission_obj.validate_unique()  # 检查当前对象在数据库是否存在唯一的
                permission_obj.save()
            except Exception as e:
                formset.errors[num].update(e)  # 把错误信息放到对应的form里面
                no_repeat_field = False

        if no_repeat_field:
            return HttpResponse('提交成功')
        else:
            return render(request, 'multi_add.html', {'formset': formset})
    return render(request, 'multi_add.html', {'formset': formset})


def multi_edit(request):
    formset_class = formset_factory(MultiUpdatePermissionForm, extra=0)  # 默认等于1,如果不想让它多增加一个,就把默认改成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():
        no_repeat_field = True
        form_list = formset.cleaned_data
        for num in range(0, formset.total_form_count()):
            form = form_list[num]
            if not form:
                continue
            permission_id = form.pop('id')
            try:
                permission_obj = models.Permission.objects.filter(id=permission_id).first()
                for key, value in form.items():
                    setattr(permission_obj, key, value)  # 更新数据库的字段
                permission_obj.validate_unique()
                permission_obj.save()
            except Exception as e:
                formset.errors[num].update(e)
                no_repeat_field = False
        if no_repeat_field:
            return HttpResponse("提交成功")
        else:
            return render(request, 'multi_edit.html', {'formset': formset})
    return render(request, 'multi_edit.html', {'formset': formset})

 

四、模板渲染

multi_add.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/4.0.0/css/bootstrap.css">
</head>
<body>

<div class="container" style="margin-top: 100px">
    <div class="row">
        <form action="" method="post" novalidate>
            {% csrf_token %}
            {{ formset.management_form }}
            <table class="table table-hover table-stripped" 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>
            <hr/>
            <input type="submit" value="提交" class="btn btn-primary">
        </form>
    </div>
</div>


</body>
</html>

multi_edit.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/4.0.0/css/bootstrap.css">
</head>
<body>

<div class="container" style="margin-top: 100px">
    <div class="row">
        <form action="" method="post" novalidate>
            {% csrf_token %}
            {{ formset.management_form }}
            <table class="table table-hover table-stripped" 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>
            <hr/>
            <input type="submit" value="提交" class="btn btn-primary">
        </form>
    </div>
</div>


</body>
</html>

edit和add的区别是edit多了个id并把id隐藏起来了

原文地址:https://www.cnblogs.com/lshedward/p/10512069.html