权限组件
一. 把自己写的对权限的处理变成一个可以随时调用的组件(步骤)
1.在项目中创建一个app
命令:startapp rbac
2.在rbac app下创建一个文件夹,service,将对权限处理的内容放到该文件夹下
(1) 为该组件做数据库准备
在models.py中:
from django.db import models class User(models.Model): name=models.CharField(max_length=32) pwd=models.CharField(max_length=32) roles=models.ManyToManyField(to="Role") def __str__(self): return self.name class Role(models.Model): title=models.CharField(max_length=32) permissions=models.ManyToManyField(to="Permission") def __str__(self): return self.title class Permission(models.Model): title=models.CharField(max_length=32) url=models.CharField(max_length=32) def __str__(self): return self.title
(2)为便于利用admin组件
在admin.py中:
from django.contrib import admin from rbac.models import * admin.site.register(User) admin.site.register(Role) class PermissionConfig(admin.ModelAdmin): list_display=["pk","title","url"] ordering = ["-pk"] admin.site.register(Permission,PermissionConfig)
(3)在service文件夹下创建middleware.py
import re from django.utils.deprecation import MiddlewareMixin from django.shortcuts import HttpResponse,redirect class SessionMiddleware(MiddlewareMixin): def process_request(self, request): path=request.path permission=request.session.get("permission") # 白名单 for reg in ["/login/","/admin/*"]: ret=re.search(reg,path) if ret: return None # 判断是否登录 user_id=request.session.get("user_id") if not user_id: return redirect("/login/") # 校验权限 for reg in permission: reg="^%s$" % reg ret=re.search(reg,path) if ret: return None return HttpResponse("无权访问")
(4)在service文件夹下创建rbac.py
from rbac.models import * def initial_sesson(request,user): permissions__url=Role.objects.filter(user__name=user).values("permissions__url") print(permissions__url) permissions__url_list=[] for item in permissions__url: print(item["permissions__url"]) permissions__url_list.append(item["permissions__url"]) print(type(permissions__url_list),permissions__url_list) request.session["permission"]=permissions__url_list
3.Settings中也要相应修改路径
MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'rbac.service.middleware.SessionMiddleware', ] 同时值得注意的是,settings中的INSTALLED_APPS,也要添加rbac INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'app01.apps.App01Config', 'rbac', 'web', ]
- 以后处理权限问题时,只需要调用inital_session即可
from django.shortcuts import render,HttpResponse from rbac.models import * from rbac.service.rbac import initial_sesson #引入 def login(request): if request.method=="GET": return render(request,"web/login.html") else: user=request.POST.get("user") pwd=request.POST.get("pwd") use=User.objects.filter(name=user,pwd=pwd).first() if use: request.session["user_id"]=use.pk initial_sesson(request,user) #调用 return HttpResponse("登录成功") else: return HttpResponse("用户名或密码错误")
二. 如何动态显示菜单权限
1.问题关键:
(1)由于角色的不同,所拥有的权限不同,左侧菜单列不同
(2)在所有的权限中,并不是全都要显示在左侧菜单列,要根据情况有一定的筛选
本次目的:增删该查,只有查作为左侧菜单显示
2.解决方案:
(1) 在permission表中新添加两个字段is_menu icon
class Permission(models.Model): title=models.CharField(max_length=32) url=models.CharField(max_length=32) is_menu=models.BooleanField(default=False) icon=models.CharField(max_length=64,default="",blank=True,null=True) def __str__(self): return self.title
(2) 在admin.py中配置让这两个字段显示
class PermissionConfig(admin.ModelAdmin): list_display=["pk","title","url","is_menu","icon"] list_editable = ["url","icon"] #注意问题:list_editable中的元素必须是list_display=中的元素 search_fields = ["title"] admin.site.register(Permission,PermissionConfig)
(3) 在rbac.py中:
当is_nemu为true时,title,icon,url三项都注入到session中;当is_nemu为false时,只有url注入到session中
from rbac.models import Role def initial_sesson(request,user): """ 功能:将当前登录人的所有权限录入session中 :param user: 当前登录人 """ # 查询当前登录人的所有权限列表 # 查看当前登录人的所有角色 # ret=Role.objects.filter(user=user) permissions = Role.objects.filter(user__name=user).values("permissions__url", "permissions__is_menu", "permissions__title", "permissions__icon", ).distinct() permission_list = [] permission_menu_list = [] for item in permissions: # 构建权限列表 permission_list.append(item["permissions__url"]) # 构建菜单权限列表 if item["permissions__is_menu"]: permission_menu_list.append({ "title":item["permissions__title"], "icon":item["permissions__icon"], "url":item["permissions__url"], })
# 将当前登录人的权限列表注入session中
request.session["permission_list"] = permission_list
# 将当前登录人的菜单权限列表注入session中
print("permission_menu_list",permission_menu_list)
request.session["permission_menu_list"] = permission_menu_list
(4)在母板中,循环request.session构建左侧菜单a标签
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>掌上大学</title> </head> <body> <div> {% for foo in request.session.permission_menu_list %} <a href="{{ foo.url }}">{{ foo.title }}</a> {% endfor %} {% block content %} {% endblock %} </div> </body> </html>
三. 为每个左侧菜单动态添加active类
缺点:要在每一个菜单视图函数中添加这样一段代码
四.(自定义过滤器(look)和标签)为每个左侧菜单动态添加active类 ,改进方式准备知识
1.自定义过滤器
自定义过滤器的步骤:
(1)检查settings中的install_app是否包含该app
(2)在APP下加一个包:必须叫templatetags
(3)建一个python脚本:名字随意
(4)在该脚本中:
from django.template import Library
register=Library() #前两句是固定的
@register.filter #让一个函数变成过滤器的语法
def mul(x):
return x*2
(5)在HTML中的应用语法:
{% load glq %} #遇到load,Django会固定循环每一个app下的templatetags语法
{{ 2|mul }} #如果有其他参数,放在函数名后,用空格隔开
(4) 写完要重启项目,过滤器才能用用
2.可以渲染标签的过滤器
(1)过滤器的编写
from django.template import Library
register=Library()
from django.utils.safestring import mark_safe #
@register.filter
def tag(val):
return mark_safe("<a href='www.baidu.com'>%s</a>" % val)
(2) HTML中的应用:
{% load glq %}
{{ '点击跳转'|tag }}
自定义过滤器的局限性:参数最多只能有两个
3.自定义标签
语法:与自定义过滤器语法大体相同
不同之处:
(1)@register.simple_tag #让一个函数变成自定义标签的语法
def mul_tag(x,y,z):
return x*y*z
(2)在HTML中:
{% load glq %}
{% mul_tag 2 3 5 %} 前者用{{ }}调用,欧后者用{% %}调用
4.includsion_tags:将母版和自定义标签结合
(1)定义includsion_tags:
@register.inclusion_tag("web/menu.html")
def get_menu_style(request):
menu_list=[11,22,33]
return {"menu_list":menu_list} #返回值尽量用字典,要不然会报错
(2)编写用到的menu.html:
<ul>
{% for foo in menu_list %}
<li>{{ foo }}</li>
{% endfor %}
</ul>
(3) 在HTML中的调用:
{% load glq %}
{% get_menu_style request %}
优点:数据之需要传一次
所以:!!!!!!!!!
对于动态添加active需求的处理
让母版调用get_menu_styles方法:
@register.inclusion_tag("web/menu.html")
def get_menu_styles(request):
permission_menu_list = request.session.get("permission_menu_list")
for item in permission_menu_list:
if re.search("^{}$".format(item["url"]), request.path):
item["class"] = "active"
return {"permission_menu_list":permission_menu_list}
menu.html:
<ul>
{% for foo in menu_list %}
<li>{{ foo }}</li>
{% endfor %}
</ul>
五.在浏览器访问一个页面的过程中render到底做了什么
六.母版继承补充:
母版继承除了可以在在所继承的母版中添加某一部分内容外,还可以在一个完整的HTML中的某一部分使用另一个HTML
语法:{% include "web/other.html" %}
七.补充细节问题:
Ajax想发送一个这样的name
一定要加” ”
如果把js抽出来,可以在HTML页面中添加一个没有用的标签,<div class={{ name }}></div>用来存储{{ class }}