一、权限分配

  需求:为用户分配角色,为角色分配权限,如下图效果:

Django - 权限分配、权限组件与CRM整合

1、视图代码:

from django.shortcuts import render
from django.http import JsonResponse

from rbac.models import User, Role, Permission

def distribute_permissions(request):
    """
    分配权限
    """
    uid = request.GET.get('uid')
    user = User.objects.filter(id=uid)
    rid = request.GET.get('rid')

    # 保存为用户分配的角色
    if request.method == 'POST' and request.POST.get('postType') == 'role':
        r_lst = request.POST.getlist("roles")
        user.first().roles.set(r_lst)

    # 保存为角色分配的权限
    if request.method == 'POST' and request.POST.get('postType') == 'permission':
        p_lst = request.POST.getlist("permissions_id")
        Role.objects.filter(pk=rid).first().permissions.set(p_lst)

    user_list = User.objects.all()
    role_list = Role.objects.all()

    if uid:   # 选中某一用户,则显示其对应角色
        role_id_list= User.objects.get(pk=uid).roles.all().values_list("pk")
        role_id_list = [item[0] for item in role_id_list]

        if rid:   # 点击某一角色,则显示其对应的权限
            per_id_list = Role.objects.filter(pk=rid).values_list("permissions__pk").distinct()
        else:  # 不点击角色,则显示选中用户对应角色拥有的权限
            per_id_list = User.objects.get(pk=uid).roles.values_list("permissions__pk").distinct()
        per_id_list = [item[0] for item in per_id_list]

    return render(request, 'distribute_permission.html', locals())

# 显示权限表,ajax请求对应的视图函数
def permissions_tree(request):
    permissions = Permission.objects.values("pk", "title", "url", "menu__title", "menu__pk", "pid_id")
    return JsonResponse(list(permissions), safe=False)

  注意:JsonResponse传入一个非字典类型的数据结构时,需要设置safe=False。

2、模板代码

  1)显示用户表相关代码

<div class="am-panel-bd">
    <ul class="user_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>

  2)显示角色表相关代码

<table class="am-table table-main mytable">
    <thead>
    <tr class="am-primary">
        <th>角色</th>
        <th>选择</th>
    </tr>
    </thead>
    <tbody>
        {% for role in role_list %}
        <tr class={% if role.id|safe == rid %}"am-danger"{% endif %}>
            {% load my_tags %}
            <td>
        <a href="?{% get_role_url request role.id %}">{{ role.title }}</a>
        </td>
            <td>
                {% if role.id in role_id_list %}
                <input type="checkbox" name="roles" value="{{ role.id }}" checked />
                {% else %}
                <input type="checkbox" name="roles" value="{{ role.id }}" />
                {% endif %}
            </td>
        </tr>
        {% endfor %}
    </tbody>
</table>

  3)显示权限表相关代码

<div class="am-panel-hd">
    <span style="margin-left: 6px;">权限分配</span>
    {% if rid %}
        <button type="submit" class="am-btn am-fr"></i>保存</button>
    {% endif %}
</div>
<p class="per_tips">提示:点击角色后才能为其分配权限</p>
<div class="am-panel-bd mypanel">
    <table class="am-table table-main mytable">
        <tbody id="tbd">
        <!-- 通过文档加载时发送ajax请求获取用户权限,ajax代码如下 -->
        </tbody>
    </table>
</div>

{% block js %}
    <script>
    $.ajax({
        url:"/rbac/permissions_tree/",
        type:"get",
        success:function (res) {
            console.log(res);
            $.each(res, function (i, permission) {   // 渲染权限树形结构
                console.log(i, permission);
                var menu_title = permission["menu__title"];
                var menu_pk = permission["menu__pk"];
                var url = permission["url"];
                var parent_id = permission["pid_id"];
                var pk = permission["pk"];
                var title = permission["title"];
                if (menu_title){   // 如果是菜单权限,即menu_id有值,pid_id为空
             // 所属的一级菜单已经存在,则直接追加权限
                    if($("#menu_"+menu_pk).length){
                        var s = `
                            <tr id="per_${pk}">
                                <td class="">
                                    <div class="">
                                        <label>
            <input name="permissions_id" value="${pk}" type="checkbox">
            <span>${title}</span>
                      </label>
                                    </div>
                                </td>
                            </tr>
                            `;
                        $("#menu_"+menu_pk).parent().append(s);
                    }else {  // 所属的一级菜单不存在,即渲染菜单,又渲染权限
                        var s = `
                            <tr class="" id="menu_${menu_pk}">
                <td>${menu_title}</td>
                 </tr>
                            <tr id="per_${pk}">
                                <td class="">
                                    <div class="">
                                        <label>
            <input name="permissions_id" value="${pk}" type="checkbox">
            <span>${title}</span>
                      </label>
                                    </div>
                                </td>
                            </tr>
                            `;
                        $("#tbd").append(s);
                    }
                }else {  // 如果不是菜单权限,即menu_id为空,pid_id有值
                    var s = `
                            <div class="">
                                <label>
            <input name="permissions_id" value="${pk}" type="checkbox">
            <span>${title}</span>
                  </label>
                            </div>
                        `;
                    $("#per_"+parent_id+" td").append(s);
                }
            });

        // 选中角色对应的权限
            var per_id_list = {{ per_id_list }}
            $.each(per_id_list, function (i, j) {
                console.log($("[value='"+j+"']")[0]);
                $("#tbd [value='"+j+"']").prop("checked", true);
            })

        }
    })
    </script>
{% endblock %}

3、思路分析

  此需求难点是将权限表渲染在页面,有两种方案:在前端处理和在后端构建数据结构。

  后端构建数据结构比较复杂,涉及到联动等。所以我们可以从前端入手,充分利用jquery的选择器,根据从数据库拿到的权限信息,用jquery技术渲染出来自己需要的结构。

二、将权限组件移植到CRM项目

(1) 先将rbac组件移植到新的项目中;

(2) 将settings中 INSTALLED_APPS 中加入"rbac";

(3) 将新项目的用户表与rbac下的User表一对一关联;

(4) 数据迁移;

(5) 在登录成功后引入rbac下的initial_session方法,做登录用户的权限信息存储(注意user对象);

(6) 在setting是中引入rbac下的权限校验中间件;

(7) 在项目的base模板中引入菜单样式,渲染显示;