知識點:css
users.html / roles.html 繼承自 base.htmlhtml
頁面滾動時,固定python
.menu { background-color: bisque; position: fixed; top: 60px; bottom: 0px; left: 0px; width: 200px; } .content { position: fixed; top: 60px; bottom: 0; right: 0; left: 200px; overflow: auto; /* 滾動條 */ }
base.html:ajax
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <!-- 引入 Bootstrap 核心 CSS 文件 --> <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> <style> .header { width: 100%; height: 60px; background-color: #336699; } .menu { background-color: bisque; position: fixed; top: 60px; bottom: 0px; left: 0px; width: 200px; } .content { position: fixed; top: 60px; bottom: 0; right: 0; left: 200px; overflow: auto; /* 滾動條 */ } </style> </head> <body> <div class="header"> {{ user.name }} </div> <div class="contain"> <div class="menu"> menu </div> <div class="content"> {% block con%} {% endblock %} </div> </div> </body> </html>
users.html:正則表達式
{% extends 'base.html' %} {% block con %} <h4>用戶列表</h4> {% for user in user_list %} <p>{{ user }}</p> {% endfor %} {% endblock con%}
roles.html:數據庫
{% extends 'base.html' %} {% block con %} <h4>角色列表</h4> <ul> {% for role in role_list %} <p>{{ role }}</p> {% endfor %} </ul> {% endblock %}
{% extends 'base.html' %} {% block con %} <h4>用戶列表</h4> <a href="/users/add" class="btn btn-primary">添加用戶</a> <table class="table table-bordered table-striped"> <thead> <tr> <th>序號</th> <th>姓名</th> <th>角色</th> <th>操做</th> </tr> </thead> <tbody> {% for user in user_list %} <tr> <td>{{ forloop.counter }}</td> <td>{{ user.name }}</td> <td> {% for role in user.roles.all %} {{ role.title }} {% endfor %} </td> <td> <a href="" class="btn btn-danger">刪除</a> <a href="" class="btn btn-warning">編輯</a> </td> </tr> {% endfor %} </tbody> </table> {% endblock %}
注意:django
(1)有一些用戶有多重角色,須要將這些角色拿到顯示在表格中的方法bootstrap
<td> {% for role in user.roles.all %} {{ role.title }} {% endfor %} </td>
(2)django模板中的forloop模板變量:在每一個`` {% for %}``循環裏有一個稱爲`` forloop`` 的模板變量。這個變量有一些提示循環進度信息的屬性。數組
forloop.counter 老是一個表示當前循環的執行次數的整數計數器。 這個計數器是從1開始的,因此在第一次循環時 forloop.counter 將會被設置爲1。瀏覽器
在頁面每每有一些功能按鈕,若是該用戶沒有權限,就不將這個按鈕開放給當前用戶,這樣處理優於向用戶提示「沒有使用權限」。
{# 根據是否有權限顯示添加用戶按鈕 #} {% if "/users/add" in permission_list %} <a href="/users/add" class="btn btn-primary">添加用戶</a> {% endif %}
處理帶有正則表達式的url:
<td> {% if '/users/delete/(d+)' in permission_list %} <a href="/users/delete/{{ user.pk }}" class="btn btn-danger">刪除</a> {% endif %} <a href="" class="btn btn-warning">編輯</a> </td>
這種方法是針對表作操做,根據表名去作判斷。
若是但願if判斷時url裏面不帶有表名字。roles和users合併用一個視圖函數來處理。
rbac/admin.py:
from django.contrib import admin # Register your models here. from .models import * class PerConfig(admin.ModelAdmin): list_display = ["title", "url"] admin.site.register(User) admin.site.register(Role) admin.site.register(Permission, PerConfig)
注意:list_display = [] 。
顯示效果:
添加一個權限組表。將每張表的增刪改查,劃到一個組裏面!不管多複雜的,最終必定是對數據庫的(增刪改查)。
修改表結構,從新處理中間件,登陸頁面的目的:全是爲了按鈕的粒度,同一個模板,同一個視圖,
顯示不一樣的數據,權限。
(1)models.py代碼
from django.db import models # Create your models here. 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) # 操做 action = models.CharField(max_length=32, default="") # 默認值爲空 # 分組 group = models.ForeignKey("PermissionGroup", default=1, on_delete=True) def __str__(self): return self.title class PermissionGroup(models.Model): title = models.CharField(max_length=32) def __str__(self): return self.title
修改完後,在一次執行數據庫遷移。
(2)再一次修改rbac/admin.py:
from django.contrib import admin # Register your models here. from .models import * class PerConfig(admin.ModelAdmin): list_display = ["title", "url", "group", "action"] admin.site.register(User) admin.site.register(Role) admin.site.register(Permission, PerConfig) admin.site.register(PermissionGroup)
(3)爲權限添加action:
所有修改後:
修改以後,GROUP描述是對哪張表進行操做,ACTION是描述對這個表作什麼操做。
(4)修改rbac_permission表的group_id信息,將角色操做類別的group_id修改成2
# -*- coding:utf-8 -*- __author__ = 'Qiushi Huang' def initial_session(user,request): """ 查看當前用戶全部的權限 :param user: :param request: :return: """ # 方案1: # permissions = user.roles.all().values("permissions__url").distinct() # print(permissions) # <QuerySet [{'permissions__url': '/users/'}, {'permissions__url': '/users/add'}]> # # permission_list = [] # for item in permissions: # permission_list.append(item["permissions__url"]) # # print(permission_list) # # request.session["permission_list"] = permission_list # 方案2: # 角色表跨到權限表查找 permissions = user.roles.all().values("permissions__url", "permissions__group_id", "permissions__action").distinct() print("permissions", permissions) # 有一個權限QuerySet中就有一個字典 """ permissions <QuerySet [{'permissions__url': '/users/', 'permissions__group_id': 1, 'permissions__action': 'list'}]> """ # 對上述數據進行處理: 以組爲鍵,以字典爲值 permission_dict = {} for item in permissions: gid = item.get("permissions__group_id") if not gid in permission_dict: permission_dict[gid] = { "urls": [item["permissions__url"], ], "actions": [item["permissions__action"], ] } else: # 組id已經在字典中 permission_dict[gid]["urls"].append(item["permissions__url"]) permission_dict[gid]["actions"].append(item["permissions__action"]) print(permission_dict) # {1: {'urls': ['/users/', '/users/add', '/users/delete/(\\d+)', '/users/edit/(\\d+)'], # 'actions': ['list', 'add', 'delete', 'edit']}} request.session['permission_dict']=permission_dict
注意:
前面是在session中註冊權限列表:
request.session['permission_list'] = permission_list
如今須要在session中註冊的是權限字典:
request.session['permission_dict'] = permission_dict
# 角色表跨到權限表查找 permissions = user.roles.all().values("permissions__url", "permissions__group_id", "permissions__action").distinct() print("permissions", permissions) # 有一個權限QuerySet中就有一個字典 """ permissions <QuerySet [{'permissions__url': '/users/', 'permissions__group_id': 1, 'permissions__action': 'list'}]> """
{ 1: { "url": ['/users/',], "actions": ['list',] }, } 若是用戶操做多個權限: { 1: { 'urls': ['/users/', '/users/add/', '/users/delete/(\\d+)/', '/users/edit/(\\d+)/'], 'actions': ['list', 'add', 'delete', 'edit'] }, } 若是除了有用戶操做權限還有角色操做權限: { 1: { 'urls': ['/users/', '/users/add/', '/users/delete/(\\d+)/', '/users/edit/(\\d+)/'], 'actions': ['list', 'add', 'delete', 'edit'] }, 2: { 'urls': ['/roles/'], 'actions': ['list'] } }
# -*- coding:utf-8 -*- __author__ = 'Qiushi Huang' import re from django.utils.deprecation import MiddlewareMixin from django.shortcuts import HttpResponse, redirect class ValidPermission(MiddlewareMixin): def process_request(self, request): # 當前訪問路徑 current_path = request.path_info # 當前路徑的屬性 ########### 檢查是否屬於白名單 ############# valid_url_list = ['/login/', '/reg/', '/admin/.*'] for valid_url in valid_url_list: ret = re.match(valid_url, current_path) if ret: return # 等同於return none ############### 檢驗是否登陸 ############## user_id = request.session.get("user_id") if not user_id: return redirect("/login/") ################ 校驗權限1 ################# # permission_list = request.session.get("permission_list") # # flag = False # for permission in permission_list: # permission = "^%s$" % permission # ret = re.match(permission, current_path) # 第一個參數是匹配規則,第二個參數是匹配項 # if ret: # flag = True # break # if not flag: # # 若是沒有訪問權限 # return HttpResponse("沒有訪問權限!") ################ 校驗權限2 ################# permission_dict = request.session.get('permission_dict') for item in permission_dict.values(): # 循環只取字典的值 urls = item["urls"] for reg in urls: reg = "^%s$" % reg ret = re.match(reg, current_path) if ret: print("actions", item["actions"]) request.actions = item["actions"] return None return HttpResponse("沒有訪問權限!")
注意:
(1)中間件的request對象,給對象添加屬性actions,將來視圖中就能夠經過request.actions拿到當前用戶對這個表的全部操做權限。
request.actions = item["actions"]
(2)數據類型從數組變爲了字典,數據處理方式略有不一樣。
class Per(object): def __init__(self, actions): self.actions = actions def add(self): return "add" in self.actions def delete(self): return "delete" in self.actions def edit(self): return "edit" in self.actions def list(self): return "list" in self.actions def users(request): user_list = User.objects.all() permission_list = request.session.get("permission_list") print(permission_list) # ['/users/', '/users/add', '/roles/', '/users/delete/(\\d+)', '/users/edit/(\\d+)'] # 查詢當前登陸人的名字 id = request.session.get("user_id") user = User.objects.filter(id=id).first() per = Per(request.actions) return render(request, "users.html", locals())
注意:
經過Per(request.actions)獲得per對象,傳到模板中能夠經過per.edit\per.list等方式來判斷是否擁有權限。增長閱讀性。
{% extends 'base.html' %} {% block con %} <h4>用戶列表</h4> {# 根據是否有權限顯示添加用戶按鈕 #} {% if per.add %} <a href="/users/add" class="btn btn-primary">添加用戶</a> {% endif %} <table class="table table-bordered table-striped"> <thead> <tr> <th>序號</th> <th>姓名</th> <th>角色</th> <th>操做</th> </tr> </thead> <tbody> {% for user in user_list %} <tr> <td>{{ forloop.counter }}</td> <td>{{ user.name }}</td> <td> {% for role in user.roles.all %} {{ role.title }} {% endfor %} </td> <td> {% if per.delete %} <a href="/users/delete/{{ user.pk }}" class="btn btn-danger">刪除</a> {% endif %} {% if per.edit %} <a href="" class="btn btn-warning">編輯</a> {% endif %} </td> </tr> {% endfor %} </tbody> </table> {% endblock %}
顯示效果:
一、權限粒度控制
簡單控制: {% if "users/add" in permissions_list%}
二、更改數據庫結構
class Permission(models.Model): title = models.CharField(max_length=32) url = models.CharField(max_length=32) # 操做 action = models.CharField(max_length=32, default="") # 默認值爲空 # 分組 group = models.ForeignKey("PermissionGroup", default=1, on_delete=True)
def __str__(self): return self.title class PermissionGroup(models.Model): title = models.CharField(max_length=32)
def __str__(self): return self.title
三、登陸驗證
permissions = user.roles.all().values("permissions__url","permissions__group_id","permissions__action").distinct()
四、構建permission_dict
五、中間件校驗權限
permission_dict = request.session.get('permission_dict') for item in permission_dict.values(): # 循環只取字典的值 urls = item["urls"] for reg in urls: reg = "^%s$" % reg ret = re.match(reg, current_path) if ret: print("actions", item["actions"]) request.actions = item["actions"] return None return HttpResponse("沒有訪問權限!")
def initial_session(user, request): """ 查看當前用戶全部的權限 :param user: :param request: :return: """ # 方案1: # permissions = user.roles.all().values("permissions__url").distinct() # print(permissions) # <QuerySet [{'permissions__url': '/users/'}, {'permissions__url': '/users/add'}]> # # permission_list = [] # for item in permissions: # permission_list.append(item["permissions__url"]) # # print(permission_list) # # request.session["permission_list"] = permission_list # 方案2: # 角色表跨到權限表查找 permissions = user.roles.all().values("permissions__url", "permissions__group_id", "permissions__action").distinct() print("permissions", permissions) # 有一個權限QuerySet中就有一個字典 """ permissions <QuerySet [{'permissions__url': '/users/', 'permissions__group_id': 1, 'permissions__action': 'list'}]> """ # 對上述數據進行處理: 以組爲鍵,以字典爲值 permission_dict = {} for item in permissions: gid = item.get("permissions__group_id") if not gid in permission_dict: permission_dict[gid] = { "urls": [item["permissions__url"], ], "actions": [item["permissions__action"], ] } else: # 組id已經在字典中 permission_dict[gid]["urls"].append(item["permissions__url"]) permission_dict[gid]["actions"].append(item["permissions__action"]) print(permission_dict) # {1: {'urls': ['/users/', '/users/add', '/users/delete/(\\d+)', '/users/edit/(\\d+)'], # 'actions': ['list', 'add', 'delete', 'edit']}} request.session['permission_dict'] = permission_dict # 註冊菜單權限 permissions = user.roles.all().values("permissions__url", "permissions__group_id", "permissions__action", "permissions__group__title").distinct() print("permissions", permissions) menu_permission_list = [] # 菜單欄中權限列表:空列表 for item in permissions: # item是裏面的字典 if item["permissions__action"] == "list": # 列表裏面套一個個的元組,每一個元組包含url和權限組title menu_permission_list.append((item["permissions__url"], item["permissions__group__title"])) print("menu_permission_list", menu_permission_list) # 註冊到session中 request.session["menu_permission_list"] = menu_permission_list
注意:
(1)註冊菜單權限:
# 註冊菜單權限 permissions = user.roles.all().values("permissions__url", "permissions__group_id", "permissions__action", "permissions__group__title").distinct()
其中permissions__group__title是跨三張表查詢。
(2)在菜單權限列表中添加元組,每一個元組包含url和權限組title信息。
menu_permission_list = [] # 菜單欄中權限列表:空列表 for item in permissions: # item是裏面的字典 if item["permissions__action"] == "list": # 列表裏面套一個個的元組,每一個元組包含url和權限組title menu_permission_list.append((item["permissions__url"], item["permissions__group__title"])) print("menu_permission_list", menu_permission_list)
(3)將菜單權限列表註冊到session中:
# 註冊到session中 request.session["menu_permission_list"] = menu_permission_list
由於模板繼承,只繼承樣式,不繼承數據!因此須要用到自定義標籤(inclusion_tag)。
在rbac項目下建立一個templatetags文夾。這個文件夾的名字必順是templatetags來命名的。而後在此文件夾下自定義一個my_tags.py文件。
from django import template register = template.Library() @register.inclusion_tag("menu.html") def get_menu(request): # 獲取當前用戶應該放到菜單欄中的權限 menu_permission_list = request.session["menu_permission_list"] return {"menu_permission_list": menu_permission_list}
它會將返回數據傳遞給模板文件menu.html.
建立menu.html模板:
<div> {% for item in menu_permission_list %} <p class="menu_btn"><a href="{{ item.0 }}">{{ item.1 }}</a></p> {% endfor %} </div>
修改base.html模板:
<body> <div class="header"> <p>{{ user.name }}</p> </div> <div class="contain"> {% load my_tags %} <div class="menu"> {% get_menu request %} </div> <div class="content"> {% block con%} {% endblock %} </div> </div> </body>
因爲rbac是可插拔組件,所以能夠將屬於權限的模板文件遷移到rbac的app中。
建立rbac/templates文件夾,將users.html / roles.html / base.html / menu.html剪切到文件夾中。
django的render去渲染 .html 時,先到項目的 templates 下找,若是找不到再到App下templates 下找,
最後找不到才報錯。
django 會根據註冊的順序顯示!
解決辦法:項目/rbac/templates/rbac/xxx.html
這時調用:return render(request, 'rbac/users.html', locals())
辦法:就是 eg:/rbac/templates/rbac/xxx.html 或者不起重名
若是 base.html 在項目下有,在App下有,先找項目下的,找不到才找App,所以全局能夠覆蓋局部的!!
知識點:路徑自動添加問題:
http://127.0.0.1:8010/users
http://127.0.0.1:8010/users/
發如今瀏覽器瀏覽時,兩個路徑均可以正常訪問到頁面。這是由於瀏覽器發請求:django 發現以後,發了一個重定向的 url 加了一個 / 因此才能匹配上:path('users/', views.users),
若是想讓django不給瀏覽器發重定向。能夠在setttings.py中添加:
APPEND_SLASH = False
在不添加這個配置時,django默認APPEND_SLASH的值爲True,django會默認的加 / 發重定向。
ajax中的url和這裏同理。