django项目--权限分组管理
2019-09-16 本文已影响0人
昆仑草莽
权限分组管理
一、权限分组列表
1. 接口设计
- 接口说明
类目 | 说明 |
---|---|
请求方法 | GET |
url定义 | /admin/groups/ |
参数格式 | 无参数 |
-
返回结果
html
2. 后端代码
-
视图
# 在myadmin/views.py中定义如下视图 class GroupListView(View): """ 分组列表视图 url:/admin/groups/ """ def get(self, request): groups = Group.objects.only('name').all() return render(request, 'myadmin/group/group_list.html', context={'groups': groups})
-
路由
# 在myadmin/urls.py中添加如下路由 path('groups/', views.GroupsView.as_view(), name='group_list')
3. 前端代码
-
html
<!-- 新建myadmin/group/group_list.html模板 --> {% extends 'admin/content_base.html' %} {% load static %} {% load news_template_filters %} {% block page_header %} 系统设置 {% endblock %} {% block page_option %} 权限分组 {% endblock %} {% block content %} <div class="box"> <div class="box-header with-border"> <h3 class="box-title">分组列表</h3> <div class="box-tools"> </div> </div> <!-- /.box-header --> <div class="box-body"> <div style="margin-bottom: 10px"> </div> <table class="table table-bordered"> <tbody> <tr> <th>#</th> <th>组名</th> <th>菜单</th> </tr> {% for group in groups %} <tr> <td style="width: 40px" data-url="{% url 'admin:update_group' group.id %}"><a href="#">{{ forloop.counter }}</a></td> <td>{{ group.name }}</td> <td> {% for permis in group.permissions.all %} {{ permis.name }}/ {% empty %} 暂未分配权限 {% endfor %} </td> </tr> {% endfor %} </tbody> </table> </div> </div> {% endblock %}
二、权限分组详情页
1.接口设计
- 接口说明:
类目 | 说明 |
---|---|
请求方法 | GET |
url定义 | /admin/group/<int:group_id>/ |
参数格式 | 路径参数 |
- 参数说明:
参数名 | 类型 | 是否必须 | 描述 |
---|---|---|---|
group_id | 整数 | 是 | 分组id |
-
返回数据
返回html表单
2.后端代码
-
视图
# 在myadmin/views.py中添加如下视图 class GroupUdateView(View): """ 分组更新视图 url: /admin/group/<int:group_id>/ """ def get(self, request, group_id): group = Group.objects.filter(id=group_id).first() if group: form = GroupModeForm(instance=group) permissions = group.permissions.only('id').all() else: form = GroupModeForm() permissions = [] menus = models.Menu.objects.only('name', 'permission_id').select_related('permission').filter(is_delete=False, parent=None) return render(request, 'myadmin/group/group_detail.html', context={ 'form': form, 'menus': menus, 'permissions': permissions })
-
路由
# 在admin/urls.py中添加如下路由 path('group/<int:group_id>/', views.GroupUdateView.as_view(), name='update_group')
-
表单
# 在 admin/forms.py中添加如下表单 class GroupModeForm(forms.ModelForm): permissions = forms.ModelMultipleChoiceField(queryset=None, required=False, help_text='权限', label='权限') def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fields['permissions'].queryset = Permission.objects.filter(menu__is_delete=False) class Meta: model = Group fields = ['name', 'permissions']
3.前端代码
-
html
<!-- 创建模板myadmin/group/group_detail.html --> {% extends 'myadmin/base/content_base.html' %} {% load static %} {% load admin_customer_tags %} {% block page_header %} 系统设置 {% endblock %} {% block page_option %} 权限分组 {% endblock %} {% block content %} <div class="box box-primary"> <div class="box-header with-border"> <h3 class="box-title">分组详情</h3> </div> <!-- /.box-header --> <!-- form start --> <div class="box-body"> <div class="row"> <div class="col-md-3"></div> <div class="col-md-6"> <form class="form-horizontal"> {% csrf_token %} {% for field in form %} {% if field.name == 'permissions' %} <div class="form-group {% if field.errors %}has-error{% endif %}"> <label for="{{ field.id_for_label }}" class="col-sm-2 control-label">{{ field.label }}</label> <div class="col-sm-10"> {% for error in field.errors %} <label class="control-label" for="{{ field.id_for_label }}">{{ error }}</label> {% endfor %} {% for menu in menus %} <div class="row" style="margin: 0"> <div class="checkbox"> <label for="menu_{{ menu.permission.id }}"> <input {% if menu.permission in permissions %}checked{% endif %} type="checkbox" name="permissions" id="menu_{{ menu.permission.id }}" value="{{ menu.permission.id }}">{{ menu.name }} </label> </div> {% for child in menu.children.all %} <div class="checkbox col-sm-offset-1"> <label for="menu_{{ child.permission.id }}"> <input type="checkbox" {% if child.permission in permissions %}checked{% endif %} name="permissions" id="menu_{{ child.permission.id }}" value="{{ child.permission.id }}">{{ child.name }} </label> </div> {% endfor %} </div> {% endfor %} </div> </div> {% else %} <div class="form-group {% if field.errors %}has-error{% endif %}"> <label for="{{ field.id_for_label }}" class="col-sm-2 control-label">{{ field.label }}</label> <div class="col-sm-10"> {% for error in field.errors %} <label class="control-label" for="{{ field.id_for_label }}">{{ error }}</label> {% endfor %} {% add_class field 'form-control' %} </div> </div> {% endif %} {% endfor %} </form> </div> <div class="col-md-3"></div> </div> </div> <div class="box-footer"> <button type="button" class="btn btn-default back">返回</button> <button type="button" data-url="{% url 'myadmin:group_update' form.instance.id %}" class="btn btn-primary pull-right save">保存 </button> </div> </div> {% endblock %}
-
js
// 创建 js/myadmin/group/group_list.js $(()=>{ // 分组详情 $('tr').each(function () { $(this).children('td:first').click(function () { $('#content').load( $(this).data('url'), (response, status, xhr) => { if (status !== 'success') { message.showError('服务器超时,请重试!') } } ); }) }); });
记得再group_list.html中引用
三、权限分组修改
1.接口设计
- 接口说明:
类目 | 说明 |
---|---|
请求方法 | PUT |
url定义 | /admin/group/<int:group_id>/ |
参数格式 | 路径参数+表单参数 |
- 参数说明:
参数名 | 类型 | 是否必须 | 描述 |
---|---|---|---|
group_id | 整数 | 是 | 分组id |
name | 字符串 | 是 | 分组名称 |
permissions | 整数 | 否 | 权限id |
-
返回数据
# 修改正常返回json数据 { "errno": "0", "errmsg": "用户修改成功!" }
如果有错误,返回html表单
2.后端代码
-
视图
# 在myadmin/views.py的GroupUpdateView视图中添加put方法 class GroupUpdateView(View): """ 分组更新视图 url:/admin/group/<int:group_id>/ """ def get(self, request, group_id): # 1. 拿到要修改的分组 group = Group.objects.filter(id=group_id).first() # 1.1 判断是否存不存在 if not group: return json_response(errno=Code.NODATA, errmsg='没有此分组!') # 2.创建表单 form = GroupModeForm(instance=group) # 3.拿到所有的可用一集菜单 menus = Menu.objects.only('name', 'permission_id').select_related('permission').filter(is_delete=False,parent=None) # 4.拿到当前组的可用权限 permissions = group.permissions.only('id').all() # 3.返回渲染html return render(request, 'myadmin/group/group_detail.html', context={ 'form': form, 'menus': menus, 'permissions': permissions }) def put(self, request, group_id): # 1. 拿到要修改的分组 group = Group.objects.filter(id=group_id).first() # 1.1 判断是否存不存在 # 1.1 判断是否存不存在 if not group: return json_response(errno=Code.NODATA, errmsg='没有此分组!') # 2. 拿到前端传递的参数 put_data = QueryDict(request.body) # 3. 校验参数 # 3.1 创建表单对象 form = GroupModeForm(put_data, instance=group) if form.is_valid(): # 4. 如果成功,保存数据 form.save() return json_response(errmsg='修改分组成功!') else: # 5. 如果失败 menus = Menu.objects.only('name', 'permission_id').select_related('permission').filter(is_delete=False, parent=None) # 4.拿到当前组的可用权限 permissions = group.permissions.only('id').all() return render(request, 'myadmin/group/group_detail.html', context={ 'form': form, 'menus': menus, 'permissions': permissions })
3.前端代码
-
js
# 创建 js/admin/group/group_detail.js $(() => { // 返回按钮 $('.box-footer button.back').click(() => { $('#content').load( $('.sidebar-menu li.active a').data('url'), (response, status, xhr) => { if (status !== 'success') { message.showError('服务器超时,请重试!') } } ); }); // 保存按钮 $('.box-footer button.save').click(function () { // 将表单中的数据进行格式化 $ .ajax({ url: $(this).data('url'), data: $('form').serialize(), type: 'PUT' }) .done((res) => { if (res.errno === '0') { message.showSuccess('修改分组成功!'); $('#content').load( $('.sidebar-menu li.active a').data('url'), (response, status, xhr) => { if (status !== 'success') { message.showError('服务器超时,请重试!') } } ); } else { $('#content').html(res) } }) .fail((res) => { message.showError('服务器超时,请重试!') }) }); // 复选框逻辑 // 点击一级菜单,二级菜单联动 // 注意要在一级菜单中class属性中加上one,二级菜单中加上two $('div.checkbox.one').each(function () { let $this = $(this); $this.find(':checkbox').click(function () { if($(this).is(':checked')){ $this.siblings('div.checkbox.two').find(':checkbox').prop('checked', true) }else{ $this.siblings('div.checkbox.two').find(':checkbox').prop('checked', false) } }) }); // 点击二级菜单,一级菜单联动 $('div.checkbox.two').each(function () { let $this = $(this); $this.find(':checkbox').click(function () { if($(this).is(':checked')){ $this.siblings('div.checkbox.one').find(':checkbox').prop('checked', true) }else { if(!$this.siblings('div.checkbox.two').find(':checkbox').is(':checked')){ $this.siblings('div.checkbox.one').find(':checkbox').prop('checked', false) } } }) }); });
四、添加分组页面
1.接口设计
1.1接口说明:
类目 | 说明 |
---|---|
请求方法 | GET |
url定义 | /admin/group/ |
参数格式 | 无参数 |
1.2返回数据
返回html表单
2.后端代码
2.1 视图
# 在admin/views.py中添加如下视图
class GroupAddView(View):
"""
添加分组视图
"""
def get(self, request):
form = GroupModeForm()
menus = models.Menu.objects.only('name', 'permission_id').select_related('permission').filter(is_delete=False,
parent=None)
return render(request, 'admin/group/group_detail.html', context={'form': form, 'menus': menus})
2.2路由
# 在admin/urls.py中添加如下路由
path('group/', views.GroupAddView.as_view(), name='add_group'),
3.前端代码
3.1html
<!-- 在admin/group/group_list.html 中添加 添加group的按钮-->
<div class="box-tools">
<button type="button" class="btn btn-primary btn-sm"
data-url="{% url 'admin:add_group' %}">添加分组
</button>
</div>
3.2js
<!-- 在admin/group/group.js 中添加 添加group的按钮的js代码如下 -->
// 添加分组
$('.box-tools button').click(function () {
$('#content').load(
$(this).data('url'),
(response, status, xhr) => {
if (status !== 'success') {
message.showError('服务器超时,请重试!')
}
}
);
});
五、添加分组
1.接口设计
1.1接口说明:
类目 | 说明 |
---|---|
请求方法 | POST |
url定义 | /admin/group/ |
参数格式 | 表单参数 |
1.2参数说明:
参数名 | 类型 | 是否必须 | 描述 |
---|---|---|---|
name | 字符串 | 是 | 分组名称 |
permissions | 整数 | 否 | 权限id |
1.3返回数据
# 修改正常返回json数据
{
"errno": "0",
"errmsg": "添加分组成功!"
}
如果有错误,返回html表单
2.后端代码
2.1 视图
# 在admin/views.py中的GroupAddView视图中添加post方法如下
class GroupAddView(View):
"""
添加分组视图
"""
def get(self, request):
form = GroupModeForm()
menus = models.Menu.objects.only('name', 'permission_id').select_related('permission').filter(is_delete=False,
parent=None)
return render(request, 'admin/group/group_detail.html', context={'form': form, 'menus': menus})
def post(self, request):
form = GroupModeForm(request.POST)
if form.is_valid():
form.save()
return json_response(errmsg='添加分组成功!')
else:
menus = models.Menu.objects.only('name', 'permission_id').select_related('permission').filter(
is_delete=False,
parent=None)
return render(request, 'admin/group/group_detail.html', context={'form': form, 'menus': menus})
3.前端代码
3.1 html
<!-- 修改group_detail.html中的保存按钮代码 -->
<button type="button" {% if form.instance.id %}
data-url="{% url 'admin:update_group' form.instance.id %}"
data-type="PUT"
{% else %}
data-url="{% url 'admin:add_group' %}"
data-type="POST"
{% endif %}
class="btn btn-primary pull-right save">保存
</button>
3.2 js
// 修改 group_detail.js中保存按钮的js代码如下
// 保存按钮
$('.box-footer button.save').click(function () {
// 将表单中的数据进行格式化
$
.ajax({
url: $(this).data('url'),
data: $('form').serialize(),
type: $(this).data('type')
})
.done((res) => {
if (res.errno === '0') {
message.showSuccess('修改分组成功!');
$('#content').load(
$('.sidebar-menu li.active a').data('url'),
(response, status, xhr) => {
if (status !== 'success') {
message.showError('服务器超时,请重试!')
}
}
);
} else {
$('#content').html(res)
}
})
.fail((res) => {
message.showError('服务器超时,请重试!')
})
});
六、权限认证整合
1.业务需求
根据django内置的权限模块功能,可以很好的进行权限认证。但是本项目大量使用ajax,在进行权限认证时会遇到麻烦。且本项目的url设计符合RESTFUL api,所以在使用内置权限认证时也会出现问题。因此本项目对权限认证做了二次开发。
2.权限认证Mixin
- Mixin类
class MyPermissionRequiredMinxin(PermissionRequiredMixin):
def has_permission(self):
"""
覆盖父类方法,解决不同请求,权限不同的问题。
"""
perms = self.get_permission_required()
if isinstance(perms, dict):
if self.request.method.lower() in perms:
return self.request.user.has_perms(perms[self.request.method.lower()])
else:
return self.request.user.has_perms(perms)
def handle_no_permission(self):
"""
覆盖父类方法,解决ajax返回json数据的问题
:return:
"""
if self.request.is_ajax():
if self.request.user.is_authenticated:
return json_response(errno=Code.ROLEERR, errmsg='您没有权限!' )
else:
return json_response(errno=Code.SESSIONERR, errmsg=
'您未登录,请登录!', data={'url': reverse(self.get_login_url())})
else:
return super().handle_no_permission()
3.视图权限认证
使用方法和django提供的权限认证方法一致,新增同一个视图通过请求方式进行权限验证的功能。
#
class MenuUpdateView(MyPermissionRequiredMinxin, View):
"""
菜单管理视图
url:/admin/menu/<int:menu_id>/
"""
# 不同请求,对应不同的权限
permission_required = {
'get': ('admin.menu_update',),
'put': ('admin.menu_update',),
'delete': ('admin.menu_delete',),
}
...
4.ajax接收处理
// 编辑菜单
$editBtns.click(function () {
let $this = $(this);
$currentMenu = $this.parent().parent();
menuId = $this.parent().data('id');
$
.ajax({
url: '/admin/menu/' + menuId + '/',
type: 'get'
})
.done((res) => {
if (res.errno === '4101') {
message.showError(res.errmsg);
setTimeout(() => {
window.location.href = res.data.url
}, 1500)
} else if (res.errno === '4105') {
message.showError(res.errmsg)
} else {
$('#modal-update .modal-content').html(res);
$('#modal-update').modal('show')
}
})
.fail(() => {
message.showError('服务器超时,请重试!')
})
});