RABC權限控制(頁面操做角色,權限和進行分配)

 上一節主要說的是如何經過url進行權限控制,這一節就說一下如何開發一個權限控制的界面,這樣咱們就能很方便的建立角色,並分配給用戶不一樣角色和不一樣權限.css

1.編寫角色管理頁面html

這個編寫較爲簡單,主要是經過modelform快速實現的,下面代碼比較簡單,我就很少說了前端

效果圖以下:node

代碼以下:python

def role_list(request):
    """角色列表"""
    roles_list = Role.objects.all()
    # 分頁
    current_page_num = request.GET.get('page')
    pagination = MyPagination(current_page_num, roles_list.count(), request)
    roles_list = roles_list[pagination.start:pagination.end]
    return render(request, 'rbac/role_list.html', {'roles_list': roles_list, 'pagination':pagination})


def role_operate(request, edit_id=None):
    """角色操做"""
    role_obj = Role.objects.filter(pk=edit_id).first()
    if request.method == "POST":
        role_form = RoleModelForm(request.POST, instance=role_obj)
        if role_form.is_valid():
            role_form.save()
            return redirect(reverse('rbac:role_list'))
        return render(request, 'rbac/role_operate.html', {'role_form': role_form})
    role_form = RoleModelForm(instance=role_obj)
    return render(request, 'rbac/role_operate.html', {'role_form': role_form, 'role_obj': role_obj})


def role_del(request, del_id):
    """刪除角色"""
    Role.objects.filter(pk=del_id).delete()
    return redirect(reverse('rbac:role_list'))
RoleModelForm,這裏就只有一個name字段,不給角色字段是建立完用戶後到時再分配
class RoleModelForm(forms.ModelForm):
    """角色的modelform"""
    class Meta:
        model = Role
        fields = ['name']

        error_messages = {
            'name': {'required': '角色名稱不能爲空'}
        }
        widgets = {
            'name': wid.TextInput(attrs={'class': 'form-control'})
        }

 

 2.編寫菜單權限管理頁面jquery

這個也不是特別難,由於主要就是兩張表在頁面渲染的事情,效果圖以下:數據庫

代碼以下:數據結構

def menu_list(request):
    """菜單權限列表"""
    # 獲取全部的菜單
    menu_list = Menu.objects.all()
    # 菜單管理目前選擇的菜單名稱id
    mid = request.GET.get('mid')
    # 若是mid有值則經過二級菜單中菜單id是一級菜單的和二級菜單下子權限id屬於一級菜單的所有找出來顯示,沒有則顯示所有菜單
    if mid:
        permission_list = Permission.objects.filter(Q(parent__menu__id=mid) | Q(menu_id=mid))
    else:
        permission_list = Permission.objects.all()
    # 查詢出權限表中的全部字段
    all_permission_list = permission_list.values('id', 'url', 'title', 'url_name',
                                                 'menu_id', 'parent_id', 'menu__title')
    # 把全部菜單以字典形式保存在字典中
    all_permission_dict = {}
    # 第一次for循環將二級菜單加入字典中
    for permission in all_permission_list:
        menu_id = permission.get('menu_id')
        # 有menu_id就證實是二級菜單,加入字典
        if menu_id:
            permission['children'] = []
            all_permission_dict[permission['id']] = permission
    # 第二次for循環將三級菜單(子權限)加入到二級菜單的children中
    for permission in all_permission_list:
        parent_id = permission.get('parent_id')
        if parent_id:
            all_permission_dict[parent_id]['children'].append(permission)
    return render(request, 'rbac/menu_list.html', {'menu_list': menu_list,
                                                   'all_permission_dict': all_permission_dict, 'mid': mid})


def menu_operate(request, edit_id=None):
    """菜單管理操做"""
    menu_obj = Menu.objects.filter(pk=edit_id).first()
    if request.method == "POST":
        form_obj = MenuModelForm(request.POST, instance=menu_obj)
        if form_obj.is_valid():
            form_obj.save()
            return redirect(reverse('rbac:menu_list'))
        return render(request, 'rbac/menu_operate.html', {'form_obj': form_obj})
    form_obj = MenuModelForm(instance=menu_obj)
    return render(request, 'rbac/menu_operate.html', {'form_obj': form_obj, 'menu_obj': menu_obj})


def menu_del(request, del_id):
    """菜單管理刪除"""
    Menu.objects.filter(pk=del_id).delete()
    return redirect(reverse('rbac:menu_list'))


def permission_operate(request, edit_id=None):
    """權限管理操做"""
    permission_obj = Permission.objects.filter(pk=edit_id).first()
    if request.method == "POST":
        form_obj = PermissionModelForm(request.POST, instance=permission_obj)
        if form_obj.is_valid():
            form_obj.save()
            return redirect(reverse('rbac:menu_list'))
        return render(request, 'rbac/permission_operate.html', {'form_obj': form_obj})
    form_obj = PermissionModelForm(instance=permission_obj)
    return render(request, 'rbac/permission_operate.html', {'form_obj': form_obj, 'permission_obj': permission_obj})


def permission_del(request, del_id):
    """權限管理刪除"""
    Permission.objects.filter(pk=del_id).delete()
    return redirect(reverse('rbac:menu_list'))

頁面對上面兩個數據的for循環展現(這也是最主要的數據展現部分)app

{% for p_permission in all_permission_dict.values %}
                        <tr class="parent" id="{{ p_permission.id }}">
                            <td class="title"><i class="fa fa-caret-down"></i>{{ p_permission.title }}</td>
                            <td>{{ p_permission.url }}</td>
                            <td>{{ p_permission.url_name }}</td>
                            <td></td>
                            <td>{{ p_permission.menu__title }}</td>
                            <td>
                                <a href="{% url 'rbac:permission_edit' p_permission.id %}"><i class="fa fa-edit"></i></a>
                                <a style="margin-left: 10px" href="{% url 'rbac:permission_del' p_permission.id %}"><i
                                        class="fa fa-trash-o text-danger"></i></a>
                            </td>
                        </tr>
                        {% for c_permission in p_permission.children %}
                            <tr pid="{{ c_permission.parent_id }}">
                                <td style="padding-left: 20px">{{ c_permission.title }}</td>
                                <td>{{ c_permission.url }}</td>
                                <td>{{ c_permission.url_name }}</td>
                                <td></td>
                                <td></td>
                                <td>
                                    <a href="{% url 'rbac:permission_edit' c_permission.id %}"><i class="fa fa-edit"></i></a>
                                    <a style="margin-left: 10px" href="{% url 'rbac:permission_del' c_permission.id %}"><i
                                            class="fa fa-trash-o text-danger"></i></a>
                                </td>
                            </tr>
                        {% endfor %}

                    {% endfor %}

點擊二級菜單顯示和隱藏,這裏巧妙的用到了二級菜單的id和子權限的parent_id相等去顯示和隱藏,下面是顯示隱藏的jquery代碼ide

<script>
        $('.permisson-area').on('click', 'tr .title', function () {
            var caret = $(this).find('i');
            var id = $(this).parent().attr('id');
            if (caret.hasClass('fa-caret-right')){
                caret.removeClass('fa-caret-right').addClass('fa-caret-down');
               $(this).parent().nextAll('tr[pid="' + id + '"]').removeClass('hide');
            }else{
                caret.removeClass('fa-caret-down').addClass('fa-caret-right');
                $(this).parent().nextAll('tr[pid=' + id + ']').addClass('hide');
            }
        })
    </script>

 

 3.分配權限管理頁面編寫(這個比較麻煩,主要是數據結構比較複雜,嵌套太多層,注意這仍是二級菜單)

實現的效果圖以下:

代碼以下,備註在代碼中寫了,詳細請看代碼:

def distribute_permissions(request):
    """分配權限"""
    # uid是前端提交的用戶id,rid是前端提交的角色id
    uid = request.GET.get('uid')
    rid = request.GET.get('rid')

    # 用戶添加角色,因爲有多個from表單因此給每一個from表單一個postType
    if request.method == 'POST' and request.POST.get('postType') == 'role' and uid:
        user = User.objects.filter(id=uid).first()
        if not user:
            return HttpResponse('用戶不存在')
        # 由於是多對多的關係,因此用set就能夠直接更新數據了,記得set裏面必須是可迭代對象,因此getlist
        user.roles.set(request.POST.getlist('roles'))
    # 角色添加權限
    if request.method == 'POST' and request.POST.get('postType') == 'permission' and rid:
        role = Role.objects.filter(id=rid).first()
        if not role:
            return HttpResponse('角色不存在')
        role.permissions.set(request.POST.getlist('permissions'))

    # 全部用戶,界面用戶展現
    user_list = User.objects.all()
    # 取得當前用戶的全部角色
    user_has_roles = User.objects.filter(id=uid).values('id', 'roles')
    # 獲取用戶擁有的角色id,數據結構是{角色id: None},這種數據結構推薦,到時直接in就能判斷了,效率高
    user_has_roles_dict = {item['roles']: None for item in user_has_roles}

    # 角色列表(全部角色),界面用戶展現
    role_list = Role.objects.all()

    # 如過選中了角色,那麼就根據角色id拿到全部的權限
    if rid:
        role_has_permissions = Role.objects.filter(id=rid).values('id', 'permissions')
    # 若是隻選中了用戶沒有選擇角色,那麼就經過用戶的角色去拿對應的全部權限
    elif uid and not rid:
        user = User.objects.filter(id=uid).first()
        if not user:
            return HttpResponse('用戶不存在')
        role_has_permissions = user.roles.values('id', 'permissions')
    else:
        # 都沒選中,就是初始化狀態,界面不勾選任何權限菜單
        role_has_permissions = []

    # 獲取角色擁有的權限id,數據結構是{權限id: None}
    role_has_permissions_dict = {item['permissions']: None for item in role_has_permissions}

    # 以列表形式存放全部的菜單信息
    all_menu_list = []

    # 查詢出全部菜單
    menu_queryset = Menu.objects.values('id', 'title')
    # 以字典形式存放全部的菜單信息
    menu_dict = {}

    # 這個for循環的做用是將一級菜單信息分別放入了menu_dict字典和all_menu_list列表中
    for item in menu_queryset:
        item['children'] = []   # 存放二級菜單(父權限)
        menu_dict[item['id']] = item    # 注意這裏是將item對象賦值給了item['id'],因此menu_dict和all_menu_list是一塊兒變化的
        all_menu_list.append(item)

    """
    下面是這兩個的數據結構,字典套字典,而後children字段子菜單就是列表,而後反覆這樣嵌套
    menu_dict = {'menu_id': {'id':1, 'title': 'xxx', 'children': [
                {'id', 'title', 'menu_id', 'children': [
                    {'id', 'title', 'parent_id'}
                ]},
                ]},
                None: {'id': None, 'title': '其餘', 'children': [{'id', 'title', 'parent_id'}]}
                }
    all_menu_list = [
        {'id':1, 'title': 'xxx', 'children': [
        {'id', 'title', 'menu_id', 'children': [
            {'id', 'title', 'parent_id'}
        ]},
        ]},
        {'id': None, 'title': '其餘', 'children': [{'id', 'title', 'parent_id'}]}
    ]
    """
    # 像首頁這些不屬於任何一級菜單,因此能夠歸屬於other下面
    other = {'id': None, 'title': '其餘', 'children': []}
    # 兩個數據結構分別加入other
    all_menu_list.append(other)
    menu_dict[None] = other

    # 查詢二級菜單的權限信息
    parent_permission = Permission.objects.filter(menu__isnull=False).values('id', 'title', 'menu_id')
    # 二級菜單信息字典
    parent_permission_dict = {}
    """
    parent_permission_dict = {父權限id: {'id', 'title', 'menu_id', 'children': [
        {'id', 'title', 'parent_id'}
    ]} }
    """

    for per in parent_permission:
        per['children'] = []    # 存放子權限
        nid = per['id']
        menu_id = per['menu_id']
        # 以二級菜單id爲鍵,二級菜單信息爲值加入到二級菜單字典中
        parent_permission_dict[nid] = per
        # 一級菜單字典將二級菜單加入到children下,注意一級菜單列表數據結構也會跟着增長(py內存使用致使)
        menu_dict[menu_id]['children'].append(per)

    # 相似上面的操做,將不是二級菜單的權限所有找出來,包括子權限和other
    node_permission = Permission.objects.filter(menu__isnull=True).values('id', 'title', 'parent_id')

    for per in node_permission:
        pid = per['parent_id']
        # 若是不是子權限,就將信息加入到other的children下
        if not pid:
            menu_dict[None]['children'].append(per)
            continue
        # 是子權限就加入到二級菜單的children下,由於menu_dict存放的是二級菜單的對象,因此此時menu_dict就有了各個層級的數據
        parent_permission_dict[pid]['children'].append(per)

    return render(request, 'rbac/distribute_permissions.html',
                  {
                      'user_list': user_list,
                      'role_list': role_list,
                      'user_has_roles_dict': user_has_roles_dict,
                      'role_has_permissions_dict': role_has_permissions_dict,
                      'all_menu_list': all_menu_list,
                      'uid': uid,
                      'rid': rid,
                  })

前端代碼:

{% extends 'layout.html' %}
{% block css %}
    <style>
        .user-area ul {
            padding-left: 20px;
        }

        .user-area li {
            cursor: pointer;
            padding: 2px 0;
        }

        .user-area li a {
            display: block;
        }

        .user-area li.active {
            font-weight: bold;
            color: red;
        }

        .user-area li.active a {
            color: red;
        }

        .role-area tr td a {
            display: block;
        }

        .role-area tr.active {
            background-color: #f1f7fd;
            border-left: 3px solid #fdc00f;
        }

        .permission-area tr.root {
            background-color: #f1f7fd;
            cursor: pointer;
        }

        .permission-area tr.root td i {
            margin: 3px;
        }

        .permission-area .node {

        }

        .permission-area .node input[type='checkbox'] {
            margin: 0 5px;
        }

        .permission-area .node .parent {
            padding: 5px 0;
        }

        .permission-area .node label {
            font-weight: normal;
            margin-bottom: 0;
            font-size: 12px;
        }

        .permission-area .node .children {
            padding: 0 0 0 20px;
        }

        .permission-area .node .children .child {
            display: inline-block;
            margin: 2px 5px;
        }

        table {
            font-size: 12px;
        }

        .panel-body {
            font-size: 12px;
        }

        .panel-body .form-control {
            font-size: 12px;
        }
    </style>
{% endblock %}
{% block content %}
    <div class="container-fluid" style="margin-top: 20px">
        <div class="col-sm-3 user-area">
            <div class="panel panel-default">
                <div class="panel-heading"><i class="fa fa-user"></i> 用戶信息</div>
                <div class="panel-body">
                    <ul>
                        {% for user in user_list %}
                            <li class={% if user.id|safe == uid %}"active"{% endif %}>
                                <a href="?uid={{ user.id }}">{{ user.name }}</a>
                            </li>
                        {% endfor %}

                    </ul>
                </div>
            </div>
        </div>
        <div class="col-sm-3 role-area">
            <form action="" method="post">
                {% csrf_token %}
                <input type="hidden" name="postType" value="role">
                <div class="panel panel-default">
                    <div class="panel-heading"><i class="fa fa-book"></i> 角色
                        {% if uid %}
                            <button type="submit" style="padding: 2px 6px;position: relative;top: -3px;"
                                    class="btn btn-success pull-right"><i style="margin-right: 2px"
                                                                          class="fa fa-save"></i> 保存
                            </button>
                        {% endif %}
                    </div>
                    <div class="panel-body">
                        <span style="color: darkgray">提示:點擊用戶後才能爲其分配角色</span>
                    </div>
                    <table class="table table-hover">
                        <thead>
                        <tr>
                            <th>角色</th>
                            <th>選擇</th>
                        </tr>
                        </thead>
                        <tbody>
                        {% load my_tag %}
                        {% for role in role_list %}
                            <tr {% if role.id|safe == rid %}class="active"{% endif %}>
                                <td><a href="?{% get_role_url request role.id %}">{{ role.name }}</a></td>
                                <td>
                                    {% if role.id in user_has_roles_dict %}
                                        <input type="checkbox" name="roles" value="{{ role.id }}" checked>
                                    {% else %}
                                        <input type="checkbox" name="roles" value="{{ role.id }}">
                                    {% endif %}
                                </td>
                            </tr>
                        {% endfor %}

                        </tbody>
                    </table>

                </div>
            </form>
        </div>
        <div class="col-sm-6 permission-area">
            <form action="" method="post">
                {% csrf_token %}
                <input type="hidden" name="postType" value="permission">
                <div class="panel panel-default">
                    <div class="panel-heading"><i class="fa fa-reddit"></i> 權限分配</div>
                    {% if rid %}
                        <button type="submit"
                                style="padding: 2px 6px;position: relative;top: -32px; margin-right: 10px;"
                                class="btn btn-success pull-right"><i style="margin-right: 2px" class="fa fa-save"></i>
                            保存
                        </button>
                    {% endif %}
                    <div class="panel-body">
                        <span style="color: darkgray">提示:點擊角色後,才能爲其分配權限</span>
                    </div>
                    <table class="table">
                        <tbody class="permission-tbody">
                        {% for item in all_menu_list %}
                            <tr class="root">
                                <td><i class="fa fa-caret-down"></i>{{ item.title }}</td>
                            </tr>
                            <tr class="node">
                                <td>
                                    {% for node in item.children %}
                                        <div class="parent">
                                            {% if node.id in role_has_permissions_dict %}
                                                <input id="permission_{{ node.id }}" name="permissions"
                                                       value="{{ node.id }}" type="checkbox" checked>
                                            {% else %}
                                                <input id="permission_{{ node.id }}" name="permissions"
                                                       value="{{ node.id }}" type="checkbox">
                                            {% endif %}
                                            <label for="permission_{{ node.id }}">{{ node.title }}</label>
                                        </div>
                                        <div class="children">
                                            {% for child in node.children %}
                                                <div class="child">
                                                    {% if child.id in role_has_permissions_dict %}
                                                        <input id="permission_{{ child.id }}" name="permissions"
                                                               value="{{ child.id }}" type="checkbox" checked>
                                                    {% else %}
                                                        <input id="permission_{{ child.id }}" name="permissions"
                                                               value="{{ child.id }}" type="checkbox">
                                                    {% endif %}
                                                    <label for="permission_{{ child.id }}">{{ child.title }}</label>
                                                </div>
                                            {% endfor %}
                                        </div>
                                    {% endfor %}
                                </td>
                            </tr>
                        {% endfor %}
                        </tbody>
                    </table>

                </div>
            </form>
        </div>
    </div>
{% endblock %}
{% block js %}
    <script>
        $('.permission-tbody').on('click', '.root', function () {
            var caret = $(this).find('i');
            if (caret.hasClass('fa-caret-right')) {
                caret.removeClass('fa-caret-right').addClass('fa-caret-down');
                $(this).next('.node').removeClass('hide');
            } else {
                caret.removeClass('fa-caret-down').addClass('fa-caret-right');
                $(this).next('.node').addClass('hide');
            }
        })
    </script>

{% endblock %}

至此,頁面大體開發完成,單獨開發完成rbac以後,還得嵌入項目中去,大體也說一下遷移的過程:

rbac應用於其餘項目流程
1.拷貝rbac到新項目
2.在settings中註冊rabc app
3.數據庫遷移
    首先先刪除原有migrations下的文件,再執行數據庫遷移命令
    python manage.py makemigrations
    python manage.py migrate
4.在根目錄下的urls.py中添加rbac相關的url
    re_path(r'^rbac/', include('rbac.urls', namespace='rbac'))
5.layout.html的建立和編寫,由於rbac中的模板都繼承了這個
6.錄入權限信息
    角色管理
    權限管理
7.分配權限
    先用戶關聯,原系統用戶表一對一關聯rbac用戶表
    from rbac.models import User
    user = models.OneToOneField(User, null=True, blank=True, on_delete=models.CASCADE)
    給用戶分角色和權限
8.登陸應用權限
    登陸成功後獲取rbac的user_obj,而後初始化用戶信息

    應用權限校驗中間件
9.注意模板layout內容和名稱

10.應用麪包屑導航欄,中間會遇到不少樣式js等不一樣,慢慢調試吧

11.權限控制到按鈕級別
相關文章
相關標籤/搜索