效果图:
一、路由配置
rbac/urls.py
from django.urls import re_path from rbac.views import menuurlpatterns = [ ... # 菜单管理 re_path(r'^menu/list/$', menu.menu_list, name='menu_list'), re_path(r'^menu/add/$', menu.menu_add, name='menu_add'), re_path(r'^menu/edit/(?P\d+)/$', menu.menu_edit, name='menu_edit'), re_path(r'^menu/del/(?P \d+)/$', menu.menu_del, name='menu_del'), ...]
二、forms表单验证
rbac/forms/menu.py
from django import formsfrom django.utils.safestring import mark_safefrom rbac import modelsICON_LIST = [ ['fa-hand-scissors-o', ''], ['fa-hand-spock-o', ''], ['fa-hand-stop-o', ''], ['fa-handshake-o', ''], ['fa-hard-of-hearing', ''], ['fa-hashtag', ''], ['fa-hdd-o', ''], ['fa-headphones', ''], ['fa-heart', ''], ['fa-heart-o', ''], ['fa-heartbeat', ''], ['fa-history', ''], ['fa-home', ''], ['fa-hotel', ''], ['fa-hourglass', ''], ['fa-hourglass-1', ''], ['fa-hourglass-2', ''], ['fa-hourglass-3', ''], ['fa-hourglass-end', ''], ['fa-hourglass-half', ''], ['fa-hourglass-o', ''], ['fa-hourglass-start', ''], ['fa-i-cursor', ''], ['fa-id-badge', ''], ['fa-id-card', ''], ['fa-id-card-o', ''], ['fa-image', ''], ['fa-mail-reply-all', ''], ['fa-reply', ''], ['fa-reply-all', ''], ['fa-retweet', ''], ['fa-wrench', '']]""" mark_safe: Explicitly mark a string as safe for (HTML) output purposes. The returned object can be used everywhere a string is appropriate."""for item in ICON_LIST: item[1] = mark_safe(item[1])class MenuModelForm(forms.ModelForm): class Meta: model = models.Menu fields = ['title', 'icon'] widgets = { 'title': forms.TextInput(attrs={ 'class': 'form-control'}), 'icon': forms.RadioSelect( choices=ICON_LIST, attrs={ 'class': 'clearfix'} ) }
三、视图函数
from django.shortcuts import HttpResponse, render, redirect, reversefrom rbac import modelsfrom rbac.forms.menu import MenuModelFormfrom rbac.service.urls import memory_reversedef menu_list(request): """ 菜单和权限列表 :param request: :return: """ menu_queryset = models.Menu.objects.all() menu_id = request.GET.get('mid') context = { 'menu_list': menu_queryset, 'menu_id': menu_id } return render(request, 'rbac/menu_list.html', context)def menu_add(request): """ 菜单和权限列表 :param request: :return: """ if request.method == 'GET': forms = MenuModelForm() return render(request, 'rbac/change.html', { 'forms': forms}) forms = MenuModelForm(data=request.POST) 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 menu_edit(request, pk): """ 编辑一级菜单 :param request: :param pk: :return: """ menu_obj = models.Menu.objects.filter(id=pk).first() if not menu_obj: return HttpResponse('菜单不存在') if request.method == 'GET': forms = MenuModelForm(instance=menu_obj) return render(request, 'rbac/change.html', { 'forms': forms}) forms = MenuModelForm(data=request.POST, instance=menu_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 menu_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.Menu.objects.filter(id=pk).delete() return redirect(menu_list_url)
memory_reverse的功能是当用户完成增删改返回列表页的时候,还带有原参数,这样回列表页的时候还会默认选中用户刚刚选中的参数
四、保留原参数
rbac/templatestags/rbac.py
...from django.template import Libraryfrom rbac.service import urlsregister = Library()@register.simple_tag()def memory_url(request, name, *args, **kwargs): """ 生成带有原搜索条件的URL(替代了模板中的url) :param request: :param name: :param args: :param kwargs: :return: """ return urls.memory_url(request, name, *args, **kwargs)...
rbac/service/urls.py
from django.http import QueryDictfrom django.shortcuts import reversedef memory_url(request, name, *args, **kwargs): # reverse用法:reverse('name', kwargs={'pk': 1}) # reverse用法:reverse('name', args=(1,)) basic_url = reverse(name, args=args, kwargs=kwargs) # 当前url中无参数 if not request.GET: return basic_url old_params = request.GET.urlencode() # 获取url中的参数 query_dict = QueryDict(mutable=True) # 提供转义功能 query_dict['_filter'] = old_params # urlencode帮我们自动转义。 # 如果不用urlencode,&符号会把这个参数分割成两个参数:_filter=mid=2 和 age=99 return '%s?%s' % (basic_url, query_dict.urlencode()) # _filter=mid=2&age=99def memory_reverse(request, name, *args, **kwargs): """ 反向生成URL http://127.0.0.1:8000/rbac/menu/edit/1/?_filter=mid%3D4 1. 在URL获取原来的搜索条件获取(filter后的值) 2. reverse生成原来的URL,如:/menu/list/ 3. /menu/list/?mid%3D4 :param request: :param name: :param args: :param kwargs: :return: """ url = reverse(name, args=args, kwargs=kwargs) original_parmas = request.GET.get('_filter') if original_parmas: url = '%s?%s' % (url, original_parmas) return url
由于需要传的参数超过了两个,所以需要用simple_tag。memory_url在模板用的。memory_reverse是在视图函数中反向解析的时候用的。
五、模板
模板层新增了菜单列表的模板,增、改页面由于新增了许多图标,所以也有一些小的变动。
rbac/templates/rbac/menu_list.html{% extends 'layout.html' %}{% load rbac %}{% block content %}{% endblock content %}一级菜单 新建
{% for menu in menu_list %} 名称 图标 选项 {% endfor %} { { menu.title }}
需要用模板语法判断用户选中的菜单并给其一个active的样式
rbac/templates/rbac/change.html
{% extends 'layout.html' %}{% block css %} {% endblock css %}{% block content %}{% endblock content %}