示例:
第一步:路由创建
rbac/urls.py
from django.urls import re_path from rbac.views import role from rbac.views import user from rbac.views import menu urlpatterns = [ # 角色管理 re_path(r'^role/list/$', role.role_list, name="role_list"), re_path(r'^role/add/$', role.role_add, name="role_add"), re_path(r'^role/edit/(?P<pk>\d+)/$', role.role_edit, name="role_edit"), re_path(r'^role/del/(?P<pk>\d+)/$', role.role_del, name="role_del"), # 用户管理 re_path(r'^user/list/$', user.user_list, name="user_list"), re_path(r'^user/add/$', user.user_add, name="user_add"), re_path(r'^user/edit/(?P<pk>\d+)/$', user.user_edit, name="user_edit"), re_path(r'^user/del/(?P<pk>\d+)/$', user.user_del, name="user_del"), re_path(r'^user/reset/password/(?P<pk>\d+)/$', user.user_reset_pwd, name="user_reset_pwd"), # 一级菜单管理 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/eidt/(?P<pk>\d+)/$', menu.menu_edit, name="menu_edit"), re_path(r'^menu/del/(?P<pk>\d+)/$', menu.menu_del, name="menu_del"), ]
第二步:ModelForm书写
rbac/forms/menu.py
from django import forms from rbac import models from django.utils.safestring import mark_safe ICON_LIST = [ ["fa-address-book", '<i class="fa fa-address-book" aria-hidden="true"></i>'], ["fa-adjust", '<i class="fa fa-adjust" aria-hidden="true"></i>'], ["fa-area-chart", '<i class="fa fa-area-chart" aria-hidden="true"></i>'], ["fa-ban", '<i class="fa fa-ban" aria-hidden="true"></i>'], ["fa-at", '<i class="fa fa-at" aria-hidden="true"></i>'], ["fa-car", '<i class="fa fa-car" aria-hidden="true"></i>'], ["fa-university", '<i class="fa fa-university" aria-hidden="true"></i>'], ["fa-bar-chart", '<i class="fa fa-bar-chart" aria-hidden="true"></i>'], ["fa-bell", '<i class="fa fa-bell" aria-hidden="true"></i>'], ["fa-bell-slash", '<i class="fa fa-bell-slash" aria-hidden="true"></i>'], ["fa-birthday-cake", '<i class="fa fa-birthday-cake" aria-hidden="true"></i>'], ["fa-book", '<i class="fa fa-book" aria-hidden="true"></i>'], ["fa-bookmark", '<i class="fa fa-bookmark" aria-hidden="true"></i>'], ["fa-bug", '<i class="fa fa-bug" aria-hidden="true"></i>'], ["fa-calendar-check-o", '<i class="fa fa-calendar-check-o" aria-hidden="true"></i>'], ["fa-cart-plus", '<i class="fa fa-cart-plus" aria-hidden="true"></i>'], ["fa-check-circle", '<i class="fa fa-check-circle" aria-hidden="true"></i>'], ["fa-times", '<i class="fa fa-times" aria-hidden="true"></i>'], ["fa-cloud-download", '<i class="fa fa-cloud-download" aria-hidden="true"></i>'], ["fa-cog", '<i class="fa fa-cog" aria-hidden="true"></i>'], ["fa-comments", '<i class="fa fa-comments" aria-hidden="true"></i>'], ["fa-copyright", '<i class="fa fa-copyright" aria-hidden="true"></i>'], ["fa-database", '<i class="fa fa-database" aria-hidden="true"></i>'], ["fa-envelope", '<i class="fa fa-envelope" aria-hidden="true"></i>'], ["fa-folder-open", '<i class="fa fa-folder-open" aria-hidden="true"></i>'], ["fa-thumbs-up", '<i class="fa fa-thumbs-up" aria-hidden="true"></i>'], ["fa-thumbs-down", '<i class="fa fa-thumbs-down" aria-hidden="true"></i>'], ["fa-users", '<i class="fa fa-users" aria-hidden="true"></i>'], ["fa-star", '<i class="fa fa-star" aria-hidden="true"></i>'], ["fa-handshake-o", '<i class="fa fa-handshake-o" aria-hidden="true"></i>'], ["fa-camera-retro", '<i class="fa fa-camera-retro" aria-hidden="true"></i>'], ["fa-fire", '<i class="fa fa-fire" aria-hidden="true"></i>'], ["fa-user-circle", '<i class="fa fa-user-circle" aria-hidden="true"></i>'], ] for item in ICON_LIST: # mark_safe()也可以让前端页面直接渲染标签 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"}), } """ icon字段,使用的是单选输入框,choices会循环一个可迭代对象,每个循环元素的第一个值作为单选框的value的值,第二个值 作为前端显示值,如:<input type="radio" value="value的值">前端显示值 """
第三步:生成带搜索条件的url和反向解析带搜索条件的url
rbac/service/urls.py
from django.urls import reverse from django.http import QueryDict def memory_reverse(request, name, *args, **kwargs): """ 反向生成URL http://127.0.0.1:8000/rbac/menu/add/?_filter=mid%3D1 1. 在url中将原来的搜索条件获取,如:_filter后的值 2. reverse生成原来的url,如:/menu/list/ 3. http://127.0.0.1:8000/rbac/menu/list/?mid=1 :param request: :param name: :param args: :param kwargs: :return: """ url = reverse(name, args=args, kwargs=kwargs) origin_params = request.GET.get("_filter") if origin_params: url = "%s?%s" % (url, origin_params) return url def memory_url(request, name, *args, **kwargs): """ 生成带有原搜索条件的URL(替代了模板中的url) http://127.0.0.1:8000/rbac/menu/list/?mid=1 http://127.0.0.1:8000/rbac/menu/add/?_filter=mid%3D1 :param request: :param name: :return: """ basic_url = reverse(name, args=args, kwargs=kwargs) # 当前url无参数 if not request.GET: return basic_url query_dict = QueryDict(mutable=True) query_dict["_filter"] = request.GET.urlencode() # 如果直接在request.GET中设置,是会报错的,所以需要用到QueryDict url = "%s?%s" % (basic_url, query_dict.urlencode()) return url
第四步:simple_tag的创建,再次封装memory_url,让其在模板中能够使用
rbac/templatetags/rbac.py
from django.template import Library from rbac.service import urls register = Library() @register.simple_tag def memory_url(request, name, *args, **kwargs): """ 生成带有原搜索条件的URL(替代了模板中的url) :param request: :param name: :return: """ return urls.memory_url(request, name, *args, **kwargs)
第五步:视图函数的书写
rbac/views/menu.py
from django.shortcuts import render, redirect, HttpResponse from rbac import models from rbac.forms.menu import MenuModelForm from rbac.service.urls import memory_reverse def menu_list(request): """ 菜单和权限列表 :param request: :return: """ menus = models.Menu.objects.all().order_by("pk") # menu_list.html的菜单名称是a标签,此a标签的href在当前url后面加了mid参数 # 取到该参数后,传给模板,模板与所循环一级菜单的id进行if比较,如果相等,那么加上'active'表示选中 menu_id = request.GET.get("mid") return render(request, "rbac/menu_list.html", {"menus": menus, "mid": menu_id}) def menu_add(request): """ 添加一级菜单 :param request: :return: """ if request.method == "GET": form = MenuModelForm() return render(request, "rbac/change.html", {"form": form}) form = MenuModelForm(data=request.POST) if form.is_valid(): form.save() # memory_reverse将之前带有原搜索条件的url取出并拼接 # /rbac/menu/add/?_filter=mid%3D1 ----> /rbac/menu/list/?mid=1 return redirect(memory_reverse(request, "rbac:menu_list")) return render(request, "rbac/change.html", {"form": form}) def menu_edit(request, pk): """ 编辑一级菜单 :param request: :param pk: :return: """ obj = models.Menu.objects.filter(id=pk).first() if not obj: return HttpResponse("菜单不存在...") if request.method == "GET": form = MenuModelForm(instance=obj) return render(request, "rbac/change.html", {"form": form}) form = MenuModelForm(instance=obj, data=request.POST) if form.is_valid(): form.save() return redirect(memory_reverse(request, "rbac:menu_list")) return render(request, "rbac/change.html", {"form": form}) def menu_del(request, pk): """ 删除一级菜单 :param request: :param pk: :return: """ url = memory_reverse(request, "rbac:menu_list") if request.method == "GET": return render(request, "rbac/delete.html", {"cancel_url": url}) models.Menu.objects.filter(id=pk).delete() return redirect(url)
第六步:模板的创建
rbac/templates/rbac/menu_list.html
{% extends 'layout.html' %} {% load rbac %} {% block css %} <style> {# 选中样式css #} tr.active { border-left: 3px solid #fdc00f; } </style> {% endblock %} {% 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 menus %} {# safe也可以转换类型,将数值类型转为了字符串类型 #} <tr class="{% if menu.id|safe == mid %}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;" href="{% memory_url request 'rbac:menu_edit' pk=menu.id %}"> <i class="fa fa-edit" aria-hidden="true"></i></a> <a style="color: #d9534f;" href="{% memory_url request 'rbac:menu_del' pk=menu.id %}"><i class="fa fa-trash-o"></i></a> </td> </tr> {% endfor %} </tbody> </table> </div> </div> </div> {% endblock %}
第七步:将渲染的图标进行样式更改
rbac/templates/rbac/change.html
{% extends 'layout.html' %} {% block css %} {# 图标样式css #} <style> ul { list-style-type: none; padding: 0; } ul li { float: left; padding: 10px; padding-left: 0; width: 80px; } ul li i { font-size: 18px; margin-left: 5px; color: #6d6565; } </style> {% endblock %} {% block content %} <div class="luffy-container"> <form class="form-horizontal" method="post" novalidate> {% csrf_token %} {% for foo in form %} <div class="form-group"> <label class="col-sm-2 control-label">{{ foo.label }}</label> <div class="col-sm-8"> {{ foo }} <span style="color: red">{{ foo.errors.0 }}</span> </div> </div> {% endfor %} <div class="form-group"> <div class="col-sm-offset-2 col-sm-8"> <input type="submit" value="保存" class="btn btn-primary"> </div> </div> </form> </div> {% endblock %}
总结:
- 此次的难点在于默认选中这个功能,将带有搜索条件的url打包成一个指定参数,然后跳转到指定页面时,取出搜索条件将其默认选中效果展示;搜索功能自己想了一下(点击分页按钮搜索条件没有了),后面有时间验证是否正确
""" 搜索如何保留原搜索条件? book/list/ 视图函数: 先判断select是否有值,如果有--利用select的值进行查询得到queryset分页传给前端,并把select的值也传给前端 如果没有--那么查询全部的queryset分页传给前端即可 模板: 搜索的input框使用get请求,如:name=select,value=搜索值;url:book/list/?select=搜索值 分页按钮的a标签,?page=变量&select=后端传入变量 重置按钮的url可以是/book/list/ """
- 将生成带有搜索条件的url和解析带有搜索条件的url两个功能函数放到了一块,但是生成有搜索条件的url功能函数需要在模板中调用,所以写成了simple_tag,在simple_tag中再导入此功能函数即可;