效果图:
三级菜单的实现和一级、二级菜单差不多。需要注意的是增加三级菜单时,三级菜单是用户提交后在后台通过二级菜单的id添加的。
一、路由分发
rbac/urls.py
... from django.urls import re_pathfrom rbac.views import menu ...urlpatterns = [...# 三级菜单re_path(r'^permission/add/(?P<second_menu_id>\d+)/$', menu.permission_add, name='permission_add'),re_path(r'^permission/edit/(?P<pk>\d+)/$', menu.permission_edit, name='permission_edit'),re_path(r'^permission/del/(?P<pk>\d+)/$', menu.permission_del, name='permission_del'),... ]
二、forms表单验证
rbac/forms/base.py
from django import formsclass BaseBootStrapForm(forms.ModelForm):def __init__(self, *args, **kwargs):super().__init__(*args, **kwargs)for name, field in self.fields.items():field.widget.attrs['class'] = 'form-control'
rbac/forms/menus.py
... from rbac import models from rbac.forms.base import BaseBootStrapForm ...... class PermissionModelForm(BaseBootStrapForm):class Meta:model = models.Permissionfields = ['title', 'name', 'url'] ...
三、视图函数
rbac/views/menu.py
from django.shortcuts import HttpResponse, render, redirect, reversefrom rbac import models from rbac.forms.menu import MenuModelForm, SecondMenuModelForm, PermissionModelForm from rbac.service.urls import memory_reversedef menu_list(request):"""菜单和权限列表:param request::return:"""menu_queryset = models.Menu.objects.all()menu_id = request.GET.get('mid') # 用户选择的一级菜单 menus_exists = models.Menu.objects.filter(id=menu_id).exists()if not menus_exists:menu_id = Noneif menu_id:second_menus = models.Permission.objects.filter(menu_id=menu_id)else:second_menus = []second_menu_id = request.GET.get('sid') # 用户选择的二级菜单 # + second_menus_exists = models.Permission.objects.filter(id=second_menu_id).exists() # +if not second_menus_exists: # +second_menu_id = None # +if second_menu_id: # +permissions = models.Permission.objects.filter(pid__id=second_menu_id) # +else: # +permissions = [] # + context = {'menu_list': menu_queryset,'menu_id': menu_id,'second_menus': second_menus,'second_menu_id': second_menu_id,'permissions': permissions # + }return render(request, 'rbac/menu_list.html', context)...# 权限的增删改def permission_add(request, second_menu_id):"""添加权限:param request::param pk::return:"""if request.method == 'GET':forms = PermissionModelForm()return render(request, 'rbac/change.html', {'forms': forms})forms = PermissionModelForm(data=request.POST)if forms.is_valid():second_menu_obj = models.Permission.objects.filter(id=second_menu_id).first()if not second_menu_obj:return HttpResponse('二级菜单不存在,请重新选择')forms.instance.pid = second_menu_obj # form.instance中包含用户提交的所有值 forms.save()url = memory_reverse(request, 'rbac:menu_list')return redirect(url)return render(request, 'rbac/change.html', {'forms': forms})def permission_edit(request, pk):"""权限编辑:param request::param pk: 要编辑的权限id:return:"""permission_obj = models.Permission.objects.filter(id=pk).first()if request.method == 'GET':forms = PermissionModelForm(instance=permission_obj)return render(request, 'rbac/change.html', {'forms': forms})forms = PermissionModelForm(data=request.POST, instance=permission_obj)if forms.is_valid():forms.save()url = memory_reverse(request, 'rbac:menu_list')return redirect(url)return render(request, 'rbac/change.html', {'forms': forms})def permission_del(request, pk):"""权限删除:param request::param pk::return:"""menu_list_url = memory_reverse(request, 'rbac:menu_list')if request.method == 'GET':return render(request, 'rbac/delete.html', {'cancel': menu_list_url})models.Permission.objects.filter(id=pk).delete()return redirect(menu_list_url)
四、模板渲染
rbac/templates/menu_list.html
{% extends 'layout.html' %} {% load rbac %}<style>tr.active {border-left: 3px solid #fdc00f;} </style>{% 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 menu_list %}<!-- 管道符可以将后端传来的整型,转换成字符串 --><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_del' 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><td>名称</td><td>CODE&URL</td><td>选项</td></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 href="{% memory_url request 'rbac:second_menu_edit' pk=second_menu.id %}"style="color:#333333;font-size:18px;"><i class="fa fa-edit" aria-hidden="true"></i></a><a href="{% memory_url request 'rbac:second_menu_del' pk=second_menu.id %}"style="color:red;font-size:18px;"><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-bookmark" aria-hidden="true">三级菜单</i>{% if menu_id %}{% 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 %}{% endif %}</div><!-- Table --><table class="table"><thead><tr><td>名称</td><td>CODE&URL</td><td>选项</td></tr></thead><tbody>{% for permission in permissions %}<tr><td rowspan="2">{{ permission.title }}</td><td>{{ permission.name }}</td><td><a href="{% memory_url request 'rbac:permission_edit' pk=permission.id %}"style="color:#333333;font-size:18px;"><i class="fa fa-edit" aria-hidden="true"></i></a><a href="{% memory_url request 'rbac:permission_del' pk=permission.id %}"style="color:red;font-size:18px;"><i class="fa fa-trash-o" aria-hidden="true"></i></a></td></tr><tr><td colspan="2" style="border-top:0;">{{ permission.url }}</td></tr>{% endfor %}</tbody></table></div></div></div>{% endblock content %}
需要注意的是选择了一级和二级菜单后才会显示三级菜单的相关信息。增删改页面无需变动。