权限组件(13):批量操作权限页面的展示和增删改查

效果图:

 

一、路由配置

rbac/urls.py
配置一个批量操作页面的路由和删除权限的路由

...
from django.urls import re_path

from rbac.views import menu
...

urlpatterns = [
    ...
    # 批量操作权限
    re_path(r'^multi/permissions/$', menu.multi_permissions, name='multi_permissions'),  # 自动发现项目中的所有URL
    re_path(r'^multi/permissions/delete/(?P<pk>\d+)', menu.multi_permissions_delete, name='multi_permissions_delete')
    ...
]

 

 

二、forms表单验证

rbac/forms/menu.py

...
from django import forms
...

...
class MultiAddPermissionForm(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.all().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 MultiEditPermissionForm(forms.Form):
    id = forms.IntegerField(
        widget=forms.HiddenInput()
    )  # 获取用户id,进行修改操作
    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')  # 是二级菜单,不能没有一级菜
...    

 

 

三、视图函数

rbac/views/menu.py

...
from django.forms.models import formset_factory
...

def
multi_permissions(request): """ 批量操作权限 :param request: :return: """ post_type = request.GET.get('type') generate_formset_class = formset_factory(MultiAddPermissionForm, extra=0) update_formset_class = formset_factory(MultiEditPermissionForm, extra=0) generate_formset = None # 出错了赋值,为了返回给页面错误信息 update_formset = None # 出错了赋值,为了返回给页面错误信息 # 批量添加 if request.method == 'POST' and post_type == 'generate': formset = generate_formset_class(data=request.POST) # 储存的所有信息,包括html标签 if formset.is_valid(): has_repeat_error = False permission_obj_list = [] url_form_list = formset.cleaned_data for num in range(0, formset.total_form_count()): url_form = url_form_list[num] # 下面的方式和model.Permission.object.create(**row)效果一样,这里用这种方式是为了捕获唯一性错误 try: permission_obj = models.Permission(**url_form) permission_obj.validate_unique() # 检查当前对象在数据库是否存在唯一的 permission_obj_list.append(permission_obj) except Exception as e: formset.errors[num].update(e) # 把错误信息放到对应的form里面 generate_formset = formset # 要把用户批量增加时出错的错误信息传给模板 has_repeat_error = True if not has_repeat_error: models.Permission.objects.bulk_create(permission_obj_list, batch_size=formset.total_form_count()) else: generate_formset = formset # 出错信息传给模板 # 批量更新 if request.method == 'POST' and post_type == 'update': formset = update_formset_class(data=request.POST) if formset.is_valid(): url_form_list = formset.cleaned_data for num in range(0, formset.total_form_count()): url_form = url_form_list[num] permission_id = url_form.pop('id') try: permission_obj = models.Permission.objects.filter(id=permission_id).first() for key, value in url_form.items(): setattr(permission_obj, key, value) permission_obj.validate_unique() permission_obj.save() except Exception as e: formset.errors[num].update(e) update_formset = formset # 要把用户批量更新时出错的错误信息传给模板 else: update_formset = formset # 出错信息传给模板 # 1. 获取项目中所有的url all_url_dict = get_all_url_dict() router_name_set = set(all_url_dict.keys()) # 所有路由中的url集合 """ set里不能有重复的值,转换成set后只会剩下key { 'rbac:menu_list': {'name': 'rbac:menu_list', 'url': 'xxxxx/yyyy/menu/list'} } 会变成 {'rbac:menu_list'} """ # # 2. 获取数据库中所有的url all_db_permissions = models.Permission.objects.all().values('id', 'title', 'name', 'url', 'menu_id', 'pid_id') db_permission_name_set = set() # 数据库中的set集合 db_permission_dict = OrderedDict() for db_permission in all_db_permissions: db_permission_dict[db_permission[ 'name']] = db_permission # {'rbac:menu_list':{'id':1,'title':'角色列表',name:'rbac:role_list',url:'/rbac/role/list'},} db_permission_name_set.add(db_permission['name']) # {'rbac:menu_list','rbac:menu_add'......} for name, value in db_permission_dict.items(): router_row_dict = all_url_dict.get(name) # {'name':'rbac:role_list','url':'/rbac/role/list'}, if not router_row_dict: # 没有别名和url的直接跳过 continue if value['url'] != router_row_dict['url']: # 数据库里的url和自动发现的url进行对比 value['url'] = '路由和数据库中的不一致' # 3. 应该添加、删除和修改的权限 # 3.1 计算出应该添加的name if not generate_formset: """ 如果目标没有通过验证,generate_formset的值就是上面出错了的formset,就不会执行下面的代码,页面就会显示错误信息 如果通过验证,就会返回给页面自动发现的数据库中有、路由中没有的url。 下面的 if not update_formset同理 """ generate_name_list = router_name_set - db_permission_name_set generate_formset = generate_formset_class( initial=[add_url for name, add_url in all_url_dict.items() if name in generate_name_list] ) # 3.2 计算出应该删除的name : 数据库有,路由中没有 delete_url_name_list = db_permission_name_set - router_name_set # 数据库里的url - 路由中的url delete_url_list = [delete_url_obj for name, delete_url_obj in db_permission_dict.items() if name in delete_url_name_list] # 3.3 计算出应该更新的name :数据库和路由中都有 if not update_formset: update_name_list = db_permission_name_set & router_name_set # 都包含的元素 update_formset = update_formset_class( initial=[update_url for name, update_url in db_permission_dict.items() if name in update_name_list] ) context = { 'generate_formset': generate_formset, 'delete_url_list': delete_url_list, 'update_formset': update_formset, } return render(request, 'rbac/multi_permissions.html', context) def multi_permissions_delete(request, pk): """ 批量页面的权限删除 :param request: :param pk: :return: """ multi_pemrission_url = memory_reverse(request, 'rbac:multi_permissions') if request.method == 'GET': return render(request, 'rbac/delete.html', {'cancel': multi_pemrission_url}) models.Permission.objects.filter(id=pk).delete() return redirect(multi_pemrission_url)

 

 

四、模板

rbac/templates/multi_permissions.html

{% extends 'layout.html' %}

{% block content %}
    <div class="luffy-container">

        <!-- 待新建的权限列表 -->
        <form action="?type=generate" method="post">
            {% csrf_token %}
            {{ generate_formset.management_form }}

            <div class="panel panel-success">
                <div class="panel-heading">
                    <i class="fa fa-th-list" aria-hidden="true">待新建的权限列表</i>
                    <button href="" class="right btn btn-primary btn-xs"
                            style="padding: 2px 8px;margin:-3px">
                        <i class="fa fa-save" aria-hidden="true">新建</i>
                    </button>
                </div>

                <table class="table">
                    <thead>
                    <tr>
                        <th>序号</th>
                        <th>名称</th>
                        <th>URL</th>
                        <th>别名</th>
                        <th>菜单</th>
                        <th>父权限</th>
                    </tr>
                    </thead>

                    <tbody>
                    {% for form in generate_formset %}
                        <tr>
                            <td>{{ forloop.counter }}</td>
                            {% for field in form %}
                                <td>{{ field }} <span style="color: red">{{ field.errors.0 }}</span></td>
                            {% endfor %}
                        </tr>
                    {% endfor %}
                    </tbody>
                </table>
            </div>
        </form>


        <!-- 数据库中有,路由中没有的权限列表 -->
        <div class="panel panel-danger">
            <!-- Default panel contents -->
            <div class="panel-heading">
                <i class="fa fa-th-list" aria-hidden="true">数据库中有,路由中没有的权限列表</i>
            </div>

            <table class="table">
                <thead>
                <tr>
                    <th>序号</th>
                    <th>名称</th>
                    <th>URL</th>
                    <th>别名</th>
                    <th>删除</th>
                </tr>
                </thead>

                <tbody>
                {% for delete_url_obj in delete_url_list %}
                    <tr>
                        <td>{{ forloop.counter }}</td>
                        <td>{{ delete_url_obj.title }}</td>
                        <td>{{ delete_url_obj.url }}</td>
                        <td>{{ delete_url_obj.name }}</td>
                        <td>
                            <a style="color: red; font-size:18px"
                               href="{% url 'rbac:multi_permissions_delete' delete_url_obj.id %}">
                                <i class="fa fa-trash-o" aria-hidden="true"></i>
                            </a>
                        </td>

                    </tr>
                {% endfor %}
                </tbody>
            </table>
        </div>

        <!-- 待更新的权限列表 -->
        <form action="?type=update" method="post">
            {% csrf_token %}
            {{ update_formset.management_form }}
            <div class="panel panel-primary">
                <div class="panel-heading">
                    <i class="fa fa-th-list" aria-hidden="true">待更新的权限列表</i>
                    <button href="" class="right btn btn-primary btn-xs"
                            style="padding: 2px 8px;margin:-3px">
                        <i class="fa fa-plus-circle" aria-hidden="true">更新</i>
                    </button>
                </div>

                <table class="table">
                    <thead>
                    <tr>
                        <th>序号</th>
                        <th>名称</th>
                        <th>URL</th>
                        <th>别名</th>
                        <th>菜单</th>
                        <th>父权限</th>
                    </tr>
                    </thead>
                    <tbody>
                    {% for form in update_formset %}
                        <tr>
                        <td>{{ forloop.counter }}</td>
                            {% 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>
            </div>
        </form>
    </div>
{% endblock content %}

 

 

在rbac/templates/menu_list.html的权限表里加上批量操作权限的按钮

{% extends 'layout.html' %}
{% load rbac %}

{% block css %}
    <style>
        tr.active {
            border-left: 3px solid #fdc00f;
        }
    </style>
{% endblock css %}

{% block content %}
    <div class="luffy-container">

        <!-- 一级菜单 -->
        <div class="col-md-3">
            <div class="panel panel-default">
                <!-- Default panel contents -->
                <div class="panel-heading">
                    <i class="fa fa-book" aria-hidden="true">一级菜单</i>
                    <a href="{% memory_url request 'rbac:menu_add' %}" class="right btn btn-success btn-xs"
                       style="padding: 2px 8px;margin:-3px">
                        <i class="fa fa-plus-circle" aria-hidden="true">新建</i>
                    </a>
                </div>
                <!-- Table -->
                <table class="table">
                    <thead>
                    <tr>
                        <th>名称</th>
                        <th>图标</th>
                        <th>选项</th>
                    </tr>
                    </thead>
                    <tbody>
                    {% for menu in menus %}
                        <!-- 管道符可以将后端传来的整型,转换成字符串 -->
                        <tr class="{% if menu.id|safe == menu_id %}active{% endif %}">
                            <td><a href="?mid={{ menu.id }}">{{ menu.title }}</a></td>
                            <td><i class="fa {{ menu.icon }}" aria-hidden="true"></i></td>
                            <td>
                                <a style="color: #333333; font-size:18px"
                                   href="{% memory_url request 'rbac:menu_edit' pk=menu.id %}">
                                    <i class="fa fa-edit" aria-hidden="true"></i>
                                </a>

                                <a style="color: red; font-size:18px"
                                   href="{% memory_url request 'rbac:menu_delete' pk=menu.id %}">
                                    <i class="fa fa-trash-o" aria-hidden="true"></i>
                                </a>
                            </td>
                        </tr>
                    {% endfor %}
                    </tbody>
                </table>
            </div>
        </div>

        <!-- 二级菜单-->
        <div class="col-md-4">
            <div class="panel panel-default">
                <!-- Default panel contents -->
                <div class="panel-heading">
                    <i class="fa fa-flag" aria-hidden="true">二级菜单</i>

                    {% if menu_id %}
                        <a href="{% memory_url request 'rbac:second_menu_add' menu_id=menu_id %}"
                           class="right btn btn-success btn-xs"
                           style="padding: 2px 8px;margin:-3px">
                            <i class="fa fa-plus-circle" aria-hidden="true">新建</i>
                        </a>
                    {% endif %}

                </div>
                <!-- Table -->
                <table class="table">
                    <thead>
                    <tr>
                        <th>名称</th>
                        <th>CODE&URL</th>
                        <th>选项</th>
                    </tr>
                    </thead>
                    <tbody>
                    {% for second_menu in second_menus %}
                        <!-- 管道符可以将后端传来的整型,转换成字符串 -->
                        <tr class="{% if second_menu.id|safe == second_menu_id %}active{% endif %}">
                            <td rowspan="2"><a
                                    href="?mid={{ menu_id }}&sid={{ second_menu.id }}">{{ second_menu.title }}</a>
                            </td>
                            <td>{{ second_menu.name }}</td>
                            <td>
                                <a style="color: #333333; font-size:18px"
                                   href="{% memory_url request 'rbac:second_menu_edit' pk=second_menu.id %}">
                                    <i class="fa fa-edit" aria-hidden="true"></i>
                                </a>

                                <a style="color: red; font-size:18px"
                                   href="{% memory_url request 'rbac:second_menu_delete' pk=second_menu.id %}">
                                    <i class="fa fa-trash-o" aria-hidden="true"></i>
                                </a>
                            </td>
                        </tr>

                        <tr class="{% if second_menu.id|safe == second_menu_id %}active{% endif %}">
                            <td colspan="2" style="border-top:0;">{{ second_menu.url }}</td>
                        </tr>
                    {% endfor %}
                    </tbody>
                </table>
            </div>
        </div>

        <!-- 权限表 -->
        <div class="col-md-5">
            <div class="panel panel-default">
                <!-- Default panel contents -->
                <div class="panel-heading">
                    <i class="fa fa-flag" aria-hidden="true">权限</i>


                    <div class="btn-group right">
                        {% if second_menu_id %}

                            <a href="{% memory_url request 'rbac:permission_add' second_menu_id=second_menu_id %}"
                               class="right btn btn-success btn-xs"
                               style="padding: 2px 8px;margin:-3px">
                                <i class="fa fa-plus-circle" aria-hidden="true">新建</i>
                            </a>
                        {% endif %}

                        <a href="{% memory_url request 'rbac:multi_permissions' %}"
                           class="btn right btn-primary btn-xs"
                           style="padding: 2px 8px;margin:-3px">
                            <i class="fa fa-plus-circle" aria-hidden="true">批量操作</i>
                        </a>

                    </div>


                    <!-- Table -->
                    <table class="table">
                        <thead>
                        <tr>
                            <th>名称</th>
                            <th>CODE&URL</th>
                            <th>选项</th>
                        </tr>
                        </thead>
                        <tbody>
                        {% for permission in permissions %}
                            <!-- 管道符可以将后端传来的整型,转换成字符串 -->
                            <tr class="">
                                <td rowspan="2">{{ permission.title }}</td>
                                <td>{{ permission.name }}</td>
                                <td>
                                    <a style="color: #333333; font-size:18px"
                                       href="{% memory_url request 'rbac:permission_edit' pk=permission.id %}">
                                        <i class="fa fa-edit" aria-hidden="true"></i>
                                    </a>

                                    <a style="color: red; font-size:18px"
                                       href="{% memory_url request 'rbac:permission_delete' pk=permission.id %}">
                                        <i class="fa fa-trash-o" aria-hidden="true"></i>
                                    </a>
                                </td>
                            </tr>

                            <tr class="">
                                <td colspan="2" style="border-top:0;">{{ permission.url }}</td>
                            </tr>
                        {% endfor %}
                        </tbody>
                    </table>
                </div>
            </div>

        </div>
    </div>


{% endblock content %}

 

posted @ 2019-03-14 10:25  梁少华  阅读(975)  评论(0编辑  收藏  举报