知識點:css
users.html / roles.html 繼承自 base.html
滑動時,固定
position: fixed;top:60px;bottom:0;left:0;width:200px;
overflow: auto;
base.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>base</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 type="text/css"> *{padding: 0;margin: 0;} .header{width: 100%; height: 60px;background-color: #336699;} .menu{background-color: bisque; position: fixed; top:60px;bottom: 0;left: 0; width: 200px;} .content{ position: fixed;top: 60px;bottom: 0; right: 0; left: 200px;overflow: auto;padding: 20px;} </style> </head> <body> <div class="header"> <p>{{ user.name }}</p> </div> <div class="container"> <div class="menu"> menu </div> <div class="content"> {% block con %} {% endblock con%} </div> </div> </body> </html>
users.htmlhtml
{% extends 'base.html' %} {% block con %} <h4>用戶列表</h4> {% for user in user_list %} <p>{{ user }}</p> {% endfor %} {% endblock con%}
知識點:git
{% 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 %}
users.htmlgithub
{% extends 'base.html' %} {% block con %} <h4>用戶列表</h4> <a href="" 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 con%}
用戶權限不一樣,按鈕顯示就不一樣!
登陸成功後,就已經註冊了session
request.session['permission_list'] = permission_list
permission_list = request.session.get('permission_list')
0.
{% if "/users/add/" in permission_list %}
<a href="/users/add/" class="btn btn-primary">添加</a>
{% endif %}
{% if '/users/delete/(\d+)/' in permission_list %}
<a href="/users/delete/{{ user.pk }}/" class="btn btn-danger">刪除</a>
{% endif %}
BUT: 很差,不想讓 if "/users/add/" 寫死,會有 "/roles/add/" 狀況,不健壯!怎麼辦?
不該該根據表名,去判斷!!
權限不一樣,按鈕顯示就不一樣 如何作呢?
上面問題的解決辦法:
爲了擴展,
# 把兩條線 合成一個線
/users/..
/roles/...
1.修改表結構
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
makemigrations
migrate
注意點:
加了一個權限組表,
將每張表的增刪改查,劃到一個組裏面!
不管多複雜的,最終必定是對數據庫的(增刪改查)
修改表結構,從新處理中間件,登陸頁面:
目的:全是爲了按鈕的粒度,同一個模板,同一個視圖,
顯示不一樣的數據,權限
models.pyajax
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) permission = 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.admin 修改
注意:list_display = []
admin.py
from django.contrib import admin 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.登陸以後,重寫 initial_session(user,request)
就是:
# 在session中註冊權限列表 用戶權限
# request.session['permission_list'] = permission_list
不該該是list 而是dict
# 在session中註冊權限字典
request.session['permission_dict'] = permission_dict
注意點:數據庫
permission = user.roles.all().values('permission__url', 'permission__group_id', 'permission__action').distinct()
對數據的處理,以組爲鍵django
{1: {'urls': ['/users/', '/users/add/', '/users/delete/(\\d+)/', '/users/edit/(\\d+)/'],
'actions': ['list', 'add', 'delete', 'edit']},
2: {'urls': ['/roles/'],
'actions': ['list']}}
permission.py
# -*- coding:utf-8 -*- def initial_session(user, request): # 方案一 # permission = user.roles.all().values('permission__url').distinct() # # print(permission) # # 去重後的 全部權限!! 將權限 存在 session 中!! # # <QuerySet [{'permission__url': '/users/'}, {'permission__url': '/users/add'}]> # # permission_list = [] # for item in permission: # permission_list.append(item['permission__url']) # # print(permission_list) # ['/users/', '/users/add'] # # # 在session中註冊權限列表 用戶權限 # request.session['permission_list'] = permission_list # 方案二 permission = user.roles.all().values('permission__url', 'permission__group_id', 'permission__action').distinct() print('permission:', permission) # permission: <QuerySet [ # {'permission__url': '/users/', # 'permission__group_id': 1, # 'permission__action': 'list'}, # {'permission__url': '/users/add/', # 'permission__group_id': 1, # 'permission__action': 'add'}, # {'permission__url': '/users/delete/(\\d+)/', # 'permission__group_id': 1, # 'permission__action': 'delete'}, # {'permission__url': '/users/edit/(\\d+)/', # 'permission__group_id': 1, # 'permission__action': 'edit'}]> # {'permission__url': 'roles/', # 'permission__group_id': 2, # 'permission__action': 'list'}]> # 處理數據 : 以組爲鍵 """ 1:{ "url":['/users/','/users/add','/users/delete/(\\d+)/','/users/edit/(\\d+)'] "action":['list','add','delete','edit'] } 2:{ "url":['/roles/'] "action":['list'] } """ permission_dict = {} for item in permission: gid = item.get('permission__group_id') url = item.get('permission__url') action = item.get('permission__action') if not gid in permission_dict.keys(): permission_dict[gid] = { "urls": [url, ], "actions": [action, ] } else: permission_dict[gid]['urls'].append(url) permission_dict[gid]['actions'].append(action) print(permission_dict) """ {1: {'urls': ['/users/', '/users/add/', '/users/delete/(\\d+)/', '/users/edit/(\\d+)/'], 'actions': ['list', 'add', 'delete', 'edit']}, 2: {'urls': ['/roles/'], 'actions': ['list']}} """ # 註冊session # # 在session中註冊權限字典 用戶權限 request.session['permission_dict'] = permission_dict
4.重寫中間件
校驗權限
# 注意:妙 !!
request.actions = item["actions"]
rbac.py
# -*- coding:utf-8 -*- from django.shortcuts import HttpResponse, render, redirect from django.utils.deprecation import MiddlewareMixin import re class ValidPermission(MiddlewareMixin): def process_request(self,request): current_path = request.path_info # 白名單,不須要任何權限的url valid_url_list = ['/login/', '/reg/', '/admin/.*'] for valid_url in valid_url_list: ret = re.match(valid_url, current_path) if ret: return # 校驗是否登陸 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: # # ['/users/', '/users/add/', '/users/edit/(\\d+)/', '/users/delete/(\\d+)/'] # # 須要 ^ $ 限定!! # permission = "^%s$" % permission # # ret = re.match(permission, current_path) # if ret: # flag = True # break # # if not flag: # return HttpResponse('無訪問權限!') # 校驗權限 2 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("action", item['actions']) # 注意:妙 !! request.actions = item["actions"] return return HttpResponse('無權訪問') """ permission_dict: {1: {'urls': ['/users/', '/users/add/', '/users/delete/(\\d+)/', '/users/edit/(\\d+)/'], 'actions': ['list', 'add', 'delete', 'edit']}, 2: {'urls': ['/roles/'], 'actions': ['list']}} """
5.重寫users()視圖,以及users.html
1.也能夠實現:
{% if "add" in request.actions %}
<a href="/users/add/" class="btn btn-primary">添加</a>
{% endif %}
BUT: 還能夠更好:用類來實現!!
{% if per.add %}
<a href="/users/add/" class="btn btn-primary">添加</a>
{% endif %}
{% if per.delete %}
<a href="" class="btn btn-danger">刪除</a>
{% endif %}
-------
per = Per(request.actions)
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
views.py
from django.shortcuts import render,HttpResponse,redirect import re from rbac.models import * from rbac.service.permission import initial_session 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() user_id = request.session.get('user_id') user = User.objects.filter(id=user_id).first() per = Per(request.actions) return render(request,'users.html',locals())
users.htmlbootstrap
{% extends 'base.html' %} {% block con %} <h4>用戶列表</h4> {# {% if "/users/add/" in permission_list %}#} {# {% if "add" in request.actions %}#} {% 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="" class="btn btn-danger">刪除</a> {% endif %} {% if per.edit %} <a href="" class="btn btn-warning">編輯</a> {% endif %} </td> </tr> {% endfor %} </tbody> </table> {% endblock con%}
不一樣的用戶,具備不一樣的權限,瀏覽器
權限不一樣,顯示的按鈕就不一樣!!session
權限粒度控制
簡單控制:
{% 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)
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
permissions:
[
{'permissions__url': '/users/add/',
'permissions__group_id': 1,
'permissions__action': 'add'},
{'permissions__url': '/roles/',
'permissions__group_id': 2,
'permissions__action': 'list'},
{'permissions__url': '/users/delete/(\\d+)',
'permissions__group_id': 1,
'permissions__action': 'delete'},
{'permissions__url': 'users/edit/(\\d+)',
'permissions__group_id': 1,
'permissions__action': 'edit'}
]
permission_dict
{
1: {
'urls': ['/users/', '/users/add/', '/users/delete/(\\d+)', 'users/edit/(\\d+)'],
'actions': ['list', 'add', 'delete', 'edit']},
2: {
'urls': ['/roles/'],
'actions': ['list']}
}
中間件校驗權限:
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("沒有訪問權限!")
思考:
菜單權限顯示
權限不一樣,菜單欄不一樣
只有查看,有必要放到菜單欄!
即:action == list 放到 菜單欄中
1.用戶登陸後,在initial_session中,註冊菜單權限
注意:permission__group__title 還能夠這樣用,跨了3張表!!
permissions = user.roles.all().values('permission__url', 'permission__action',
'permission__group__title').distinct()
menu_permission_list = []
for item in permissions:
if item['permission__action'] == 'list':
menu_permission_list.append((item['permission__url'], item['permission__group__title']))
print(menu_permission_list)
# [('/users/', '用戶管理'), ('/roles/', '角色管理')]
request.session['menu_permission_list'] = menu_permission_list
2.menu
menu_permission_list = request.session.get('menu_permission_list')
base.html
{% for item in menu_permission_list %}
<p class="menu_btn"><a href="{{ item.0 }}">{{ item.1 }}</a></p>
{% endfor %}
能夠實現,菜單顯示!可是不行,爲何?
由於模板繼承,只繼承樣式,不繼承數據!全部須要用到 自定義標籤(inclusion_tag)
3.自定義標籤(inclusion_tag)
rbac/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.get('menu_permission_list')
return {'menu_permission_list':menu_permission_list}
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
{% load my_tags %}
<div class="menu">
{% get_menu request %}
</div>
4.包...建在哪一個App
屬於權限的就建在rbac的APP裏,由於rpac最後是可插拔的組件!!
users.html / roles.html / base.html / menu.html
是和權限相關的,因此應該放在 rbac/templates/... 方便之後copy走!!
djangod的render去渲染 .html 時,先到項目的 templates 下找,找不到,再到App下 templates 下找,
最後找不到,才報錯!!
若是多個App的templates 下的.html重名怎麼辦? django 會根據註冊的順序顯示!
解決辦法:
項目/rbac/templates/rbac/xxx.html
這時調用:
return render(request, 'rbac/users.html', locals())
注意:
templates 或者 templatetag 注意多個app下面 的文件名 有可能都會重名!!
辦法:就是 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不給瀏覽器發重定向,不加 /
配置:
APPEND_SLASH = False
APPEND_SLASH = True # 默認爲 True
ajax 也是,django會默認的加 / 發重定向
原始版