python 全栈开发,Day108(客户管理之权限控制,客户管理之动态"一级"菜单,其他应用使用rbac组件,django static文件的引入方式)
一、客户管理之权限控制
昨天的作业,有很多不完善的地方
下载代码,基本实现权限验证
https://github.com/987334176/luffy_permission/archive/v1.2.zip
必须下载此代码,否则下面的不用看了!!!
补充说明:
admin后台
查看rbac目录下的admin.py,看下面几行代码
class PermissionAdmin(admin.ModelAdmin):
list_display = ['title','url'] # 显示的字段
list_editable = ['url'] # 允许编辑 admin.site.register(models.Permission,PermissionAdmin)
它能实现下面的效果:
url可以直接编辑。注意url的最前面,必须包含"/",为什么呢?后面做菜单的时候,会用到!
session问题
查看web目录-->views目录下的account.py,看这一行
permission_list = obj.roles.filter(permissions__url__isnull=False).values('permissions__url').distinct()
有些用户,是没有角色的。需要使用permissions__url__isnull=False 过滤掉为空的记录
由于权限判断,只需要url。所以取url字段,也就是 values('permissions__url')
由于用户可能有多个角色,那么url必然有重复的。使用 distinct() 做去重!
在django中session存储时,默认对数据做序列化。所以数据不能是queryset对象,使用list进行强制转换!
权限在很多地方会用到,为了避免key放生变动,导致到处代码改动。将key名放在settings.py中。
正则匹配
新建一个11.py文件,内容如下:
current_url = '/customer/edit/1/' reg = "^/customer/edit/(\d+)/$" import re
result = re.match(reg,current_url)
print(result)
执行输出:
<_sre.SRE_Match object; span=(0, 17), match='/customer/edit/1/'>
输入一个不匹配的
current_url = '/customer/edit/1/dfasfsda'
reg = "^/customer/edit/(\d+)/$" import re
result = re.match(reg,current_url)
print(result)
执行输出:None
注意:这里必须使用match,它表示从头到尾匹配。不能使用search和findall
search表示有多个结果时,只输出第一个!
中间件权限校验
查看web目录-->middleware目录-->rbac.py,看这一行代码
reg = "^%s$" % item.get('permissions__url')
它表示为每一个url增加开始符号"^"和结束符号"$"
这样,就能精确匹配了!
对于白名单,也就是不需要进行权限验证的。写在settings.py中,方便扩展!
二、客户管理之动态"一级"菜单
上述过程中的菜单是在程序中定义好的,无法根据用户权限进行动态配置。
那么,接下来我们来完成一个 “单级菜单”的功能:
表设计
看下面的url,有些可以成为菜单,有些不能!
上面红色方框的,就是菜单。其他的,比如带参数的。它是动态链接,因此不能成为菜单!
那么如何区分呢?给权限表加2个字段,一个表示是否可做菜单,一个是菜单的图标。
进入目录rbac,修改models.py
from django.db import models class Permission(models.Model):
"""
权限表
"""
title = models.CharField(verbose_name='标题', max_length=32)
url = models.CharField(verbose_name='含正则的URL', max_length=128) is_menu = models.BooleanField(verbose_name='是否可做菜单', default=False)
icon = models.CharField(verbose_name='菜单图标',max_length=32, null=True, blank=True) def __str__(self):
return self.title class Role(models.Model):
"""
角色
"""
title = models.CharField(verbose_name='角色名称', max_length=32)
permissions = models.ManyToManyField(verbose_name='拥有的所有权限', to='Permission', blank=True) def __str__(self):
return self.title class UserInfo(models.Model):
"""
用户表
"""
name = models.CharField(verbose_name='用户名', max_length=32)
password = models.CharField(verbose_name='密码', max_length=64)
email = models.CharField(verbose_name='邮箱', max_length=32)
roles = models.ManyToManyField(verbose_name='拥有的所有角色', to='Role', blank=True) def __str__(self):
return self.name
执行2个命令,生成字段
python manage.py makemigrations
python manage.py migrate
font awesome
一套绝佳的图标字体库和CSS框架,Font Awesome为您提供可缩放的矢量图标,您可以使用CSS所提供的所有特性对它们进行更改,包括:大小、颜色、阴影或者其它任何支持的效果。
官方网址:
http://fontawesome.dashgame.com/
使用方式
将以下代码粘贴到网页HTML代码的 <head> 部分
<link href="//netdna.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
基本图标
您可以将Font Awesome图标使用在几乎任何地方,只需要使用CSS前缀 fa
,再加上图标名称。 Font Awesome是为使用内联元素而设计的。我们通常更喜欢使用 <i>
,因为它更简洁。 但实际上使用 <span>
才能更加语义化。
<i class="fa fa-camera-retro"></i> fa-camera-retro
搜索图标
官方没有提供图标搜索功能,这里提供一个网站,可以搜索你想要的图标
https://www.thinkcmf.com/font/search.html
录入数据
还有一个
修改orm
修改web目录-->views-->account.py
from django.shortcuts import render, redirect, HttpResponse
from rbac import models
from django.conf import settings def login(request):
"""
用户登陆
:param request:
:return:
"""
if request.method == 'GET':
return render(request, 'login.html') # 1. 获取提交的用户名和密码
user = request.POST.get('user')
pwd = request.POST.get('pwd') # 2. 检验用户是否合法
obj = models.UserInfo.objects.filter(name=user, password=pwd).first()
if not obj:
return render(request, 'login.html', {'msg': '用户名或密码错误'}) # 3. 获取用户信息和权限信息写入session
permission_queryset = obj.roles.filter(permissions__url__isnull=False).values('permissions__url',
'permissions__is_menu',
'permissions__title',
'permissions__icon',
).distinct() menu_list = [] # 菜单列表
permission_list = [] # 权限列表 for row in permission_queryset:
permission_list.append({'permissions__url': row['permissions__url']}) if row['permissions__is_menu']:
menu_list.append(
{'title': row['permissions__title'], 'icon': row['permissions__icon'],
'url': row['permissions__url']}) request.session[settings.PERMISSION_SESSION_KEY] = permission_list
request.session[settings.MENU_SESSION_KEY] = menu_list return redirect('/customer/list/')
修改settings.py,增加变量
########################## 权限相关 #######################
PERMISSION_SESSION_KEY = "permission_list"
MENU_SESSION_KEY = "menu_list"
VALID_URL = [
'^/login/$',
'^/admin/.*',
]
通过这样,权限和菜单,就分开了。各不影响!
菜单展示
菜单在是web目录-->templates目录-->layout.html里面的
对于每个用户展示不同的菜单,需要分离出来。需要使用自定义标签来渲染!
自定义标签
在web目录下面创建templatetags文件夹,在此文件夹中创建rbac.py
from django.template import Library
from django.conf import settings
import re
register = Library() @register.inclusion_tag('menu.html')
def menu(request):
"""
生成菜单
:param request:
:return:
""" # 获取session中的菜单列表
menu_list = request.session.get(settings.MENU_SESSION_KEY) for item in menu_list:
# 每一个url增加^$,比如/customer/list/变成^/customer/list/$
reg = "^%s$" % item['url']
if re.match(reg,request.path_info): # 判断当前路径是否匹配
# 增加一个样式,class为action。表示选中状态
item['class'] = 'active' return {'menu_list':menu_list} # 变量传给模板
在web目录-->templates目录下,创建menu.html
<div class="static-menu">
{% for item in menu_list %}
<a href="{{ item.url }}" class="{{ item.class }}">
<span class="icon-wrap"><i class="fa {{ item.icon }}"></i></span> {{ item.title }}</a>
{% endfor %}
</div>
修改web目录-->templates目录-->layout.html,使用自定义标签
{% load staticfiles %}
{% load rbac %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>路飞学城</title>
<link rel="shortcut icon" href="{% static 'imgs/luffy-study-logo.png' %} ">
<link rel="stylesheet" href="{% static 'plugins/bootstrap/css/bootstrap.css' %} "/>
<link rel="stylesheet" href="{% static 'plugins/font-awesome/css/font-awesome.css' %} "/>
<link rel="stylesheet" href="{% static 'css/commons.css' %} "/>
<link rel="stylesheet" href="{% static 'css/nav.css' %} "/>
<style>
body {
margin: 0;
} .no-radius {
border-radius: 0;
} .no-margin {
margin: 0;
} .pg-body > .left-menu {
background-color: #EAEDF1;
position: absolute;
left: 1px;
top: 48px;
bottom: 0;
width: 220px;
border: 1px solid #EAEDF1;
overflow: auto;
} .pg-body > .right-body {
position: absolute;
left: 225px;
right: 0;
top: 48px;
bottom: 0;
overflow: scroll;
border: 1px solid #ddd;
border-top: 0;
font-size: 13px;
min-width: 755px;
} .navbar-right {
float: right !important;
margin-right: -15px;
} .luffy-container {
padding: 15px;
} .left-menu .menu-body .static-menu { } .left-menu .menu-body .static-menu .icon-wrap {
width: 20px;
display: inline-block;
text-align: center;
} .left-menu .menu-body .static-menu a {
text-decoration: none;
padding: 8px 15px;
border-bottom: 1px solid #ccc;
color: #333;
display: block;
background: #efefef;
background: -webkit-gradient(linear, left bottom, left top, color-stop(0, #efefef), color-stop(1, #fafafa));
background: -ms-linear-gradient(bottom, #efefef, #fafafa);
background: -moz-linear-gradient(center bottom, #efefef 0%, #fafafa 100%);
background: -o-linear-gradient(bottom, #efefef, #fafafa);
filter: progid:dximagetransform.microsoft.gradient(startColorStr='#e3e3e3', EndColorStr='#ffffff');
-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#fafafa',EndColorStr='#efefef')";
box-shadow: inset 0px 1px 1px white;
} .left-menu .menu-body .static-menu a:hover {
color: #2F72AB;
border-left: 2px solid #2F72AB;
} .left-menu .menu-body .static-menu a.active {
color: #2F72AB;
border-left: 2px solid #2F72AB;
}
</style>
</head>
<body> <div class="pg-header">
<div class="nav">
<div class="logo-area left">
<a href="#">
<img class="logo" src="{% static 'imgs/logo.svg' %}">
<span style="font-size: 18px;">路飞学城 </span>
</a>
</div> <div class="left-menu left">
<a class="menu-item">资产管理</a>
<a class="menu-item">用户信息</a>
<a class="menu-item">路飞管理</a>
<div class="menu-item">
<span>使用说明</span>
<i class="fa fa-caret-down" aria-hidden="true"></i>
<div class="more-info">
<a href="#" class="more-item">管他什么菜单</a>
<a href="#" class="more-item">实在是编不了</a>
</div>
</div>
</div> <div class="right-menu right clearfix"> <div class="user-info right">
<a href="#" class="avatar">
<img class="img-circle" src="{% static 'imgs/default.png' %}">
</a> <div class="more-info">
<a href="#" class="more-item">个人信息</a>
<a href="#" class="more-item">注销</a>
</div>
</div> <a class="user-menu right">
消息
<i class="fa fa-commenting-o" aria-hidden="true"></i>
<span class="badge bg-success">2</span>
</a> <a class="user-menu right">
通知
<i class="fa fa-envelope-o" aria-hidden="true"></i>
<span class="badge bg-success">2</span>
</a> <a class="user-menu right">
任务
<i class="fa fa-bell-o" aria-hidden="true"></i>
<span class="badge bg-danger">4</span>
</a>
</div> </div>
</div>
<div class="pg-body">
<div class="left-menu">
<div class="menu-body">
{% menu request %} </div>
</div>
<div class="right-body">
<div>
<ol class="breadcrumb no-radius no-margin" style="border-bottom: 1px solid #ddd;"> <li><a href="#">首页</a></li>
<li class="active">客户管理</li> </ol>
</div>
{% block content %} {% endblock %}
</div>
</div> <script src="{% static 'js/jquery-3.3.1.min.js' %} "></script>
<script src="{% static 'plugins/bootstrap/js/bootstrap.js' %} "></script>
{% block js %} {% endblock %}
</body>
</html>
使用完全权限的用户,登录页面,效果如下:
使用非完全权限的用户登录
效果如下:
通过这样,就可以不同的用户登录,动态的展示菜单!
解耦代码
那么问题来了,在web应用中。设计到权限的,都在此目录中。这样是不合理的,得需要把相关代码移植到rbac应用中!
将web目录下的middleware文件夹,剪贴到rbac目录中
修改settings.py,修改中间件的路径
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.middleware.rbac.RbacMiddleware',
]
在rbac目录下,创建文件夹service,在此路径下创建文件init_permission.py,表示权限初始化
from django.conf import settings def init_permission(request,user):
"""
权限和菜单信息初始化,以后使用时,需要在登陆成功后调用该方法将权限和菜单信息放入session
:param request:
:param user:
:return:
""" # 3. 获取用户信息和权限信息写入session
permission_queryset = user.roles.filter(permissions__url__isnull=False).values('permissions__url',
'permissions__is_menu',
'permissions__title',
'permissions__icon',
).distinct() menu_list = []
permission_list = [] for row in permission_queryset:
permission_list.append({'permissions__url': row['permissions__url']}) if row['permissions__is_menu']:
menu_list.append(
{'title': row['permissions__title'], 'icon': row['permissions__icon'], 'url': row['permissions__url']}) request.session[settings.PERMISSION_SESSION_KEY] = permission_list
request.session[settings.MENU_SESSION_KEY] = menu_list
修改web目录-->views目录-->account.py
from django.shortcuts import render, redirect, HttpResponse
from rbac import models
from rbac.service.init_permission import init_permission
from django.conf import settings def login(request):
"""
用户登陆
:param request:
:return:
"""
if request.method == 'GET':
return render(request, 'login.html') # 1. 获取提交的用户名和密码
user = request.POST.get('user')
pwd = request.POST.get('pwd') # 2. 检验用户是否合法
obj = models.UserInfo.objects.filter(name=user, password=pwd).first()
if not obj:
return render(request, 'login.html', {'msg': '用户名或密码错误'}) # 写入session
request.session['user_info'] = {'id': obj.id, 'name': obj.name}
# 初始化权限,并写入session
init_permission(request, obj) return redirect('/customer/list/')
在rbac目录-->创建templates-->创建rbac文件夹,避免文件冲突。将web目录-->templates里面的menu.html剪切过来
将web目录下的templatetags文件夹剪贴到rbac目录下
修改rbac目录-->templatetags-->rbac.py,更改模板路径
from django.template import Library
from django.conf import settings
import re
register = Library() @register.inclusion_tag('rbac/menu.html')
def menu(request):
"""
生成菜单
:param request:
:return:
""" # 获取session中的菜单列表
menu_list = request.session.get(settings.MENU_SESSION_KEY) for item in menu_list:
# 每一个url增加^$,比如/customer/list/变成^/customer/list/$
reg = "^%s$" % item['url']
if re.match(reg,request.path_info): # 判断当前路径是否匹配
# 增加一个样式,class为action。表示选中状态
item['class'] = 'active' return {'menu_list':menu_list} # 变量传给模板
在rbac目录-->创建目录static-->创建目录rbac,在此文件下创建文件rbac.css
.static-menu { } .static-menu .icon-wrap {
width: 20px;
display: inline-block;
text-align: center;
} .static-menu a {
text-decoration: none;
padding: 8px 15px;
border-bottom: 1px solid #ccc;
color: #333;
display: block;
background: #efefef;
background: -webkit-gradient(linear, left bottom, left top, color-stop(0, #efefef), color-stop(1, #fafafa));
background: -ms-linear-gradient(bottom, #efefef, #fafafa);
background: -moz-linear-gradient(center bottom, #efefef 0%, #fafafa 100%);
background: -o-linear-gradient(bottom, #efefef, #fafafa);
filter: progid:dximagetransform.microsoft.gradient(startColorStr='#e3e3e3', EndColorStr='#ffffff');
-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#fafafa',EndColorStr='#efefef')";
box-shadow: inset 0px 1px 1px white;
} .static-menu a:hover {
color: #2F72AB;
border-left: 2px solid #2F72AB;
} .static-menu a.active {
color: #2F72AB;
border-left: 2px solid #2F72AB;
}
修改web目录-->templates-->layout.html。删除多余的css文件
{% load staticfiles %}
{% load rbac %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>路飞学城</title>
<link rel="shortcut icon" href="{% static 'imgs/luffy-study-logo.png' %} ">
<link rel="stylesheet" href="{% static 'plugins/bootstrap/css/bootstrap.css' %} "/>
<link rel="stylesheet" href="{% static 'plugins/font-awesome/css/font-awesome.css' %} "/>
<link rel="stylesheet" href="{% static 'css/commons.css' %} "/>
<link rel="stylesheet" href="{% static 'css/nav.css' %} "/>
<link rel="stylesheet" href="{% static 'rbac/rbac.css' %} "/>
<style>
body {
margin: 0;
} .no-radius {
border-radius: 0;
} .no-margin {
margin: 0;
} .pg-body > .left-menu {
background-color: #EAEDF1;
position: absolute;
left: 1px;
top: 48px;
bottom: 0;
width: 220px;
border: 1px solid #EAEDF1;
overflow: auto;
} .pg-body > .right-body {
position: absolute;
left: 225px;
right: 0;
top: 48px;
bottom: 0;
overflow: scroll;
border: 1px solid #ddd;
border-top: 0;
font-size: 13px;
min-width: 755px;
} .navbar-right {
float: right !important;
margin-right: -15px;
} .luffy-container {
padding: 15px;
} </style>
</head>
<body> <div class="pg-header">
<div class="nav">
<div class="logo-area left">
<a href="#">
<img class="logo" src="{% static 'imgs/logo.svg' %}">
<span style="font-size: 18px;">路飞学城 </span>
</a>
</div> <div class="left-menu left">
<a class="menu-item">资产管理</a>
<a class="menu-item">用户信息</a>
<a class="menu-item">路飞管理</a>
<div class="menu-item">
<span>使用说明</span>
<i class="fa fa-caret-down" aria-hidden="true"></i>
<div class="more-info">
<a href="#" class="more-item">管他什么菜单</a>
<a href="#" class="more-item">实在是编不了</a>
</div>
</div>
</div> <div class="right-menu right clearfix"> <div class="user-info right">
<a href="#" class="avatar">
<img class="img-circle" src="{% static 'imgs/default.png' %}">
</a> <div class="more-info">
<a href="#" class="more-item">个人信息</a>
<a href="#" class="more-item">注销</a>
</div>
</div> <a class="user-menu right">
消息
<i class="fa fa-commenting-o" aria-hidden="true"></i>
<span class="badge bg-success">2</span>
</a> <a class="user-menu right">
通知
<i class="fa fa-envelope-o" aria-hidden="true"></i>
<span class="badge bg-success">2</span>
</a> <a class="user-menu right">
任务
<i class="fa fa-bell-o" aria-hidden="true"></i>
<span class="badge bg-danger">4</span>
</a>
</div> </div>
</div>
<div class="pg-body">
<div class="left-menu">
<div class="menu-body">
{% menu request %}
</div>
</div>
<div class="right-body">
<div>
<ol class="breadcrumb no-radius no-margin" style="border-bottom: 1px solid #ddd;"> <li><a href="#">首页</a></li>
<li class="active">客户管理</li> </ol>
</div>
{% block content %} {% endblock %}
</div>
</div> <script src="{% static 'js/jquery-3.3.1.min.js' %} "></script>
<script src="{% static 'plugins/bootstrap/js/bootstrap.js' %} "></script>
{% block js %} {% endblock %}
</body>
</html>
重启django项目,重新登录,效果同上!
rbac组件就独立出来了,以后想用的话,复制整个文件夹。
并做一些相关配置,就可以使用了!
完整代码,请访问github
https://github.com/987334176/luffy_permission/archive/v1.4.zip
三、其他应用使用rbac组件
创建新项目
新建一个项目untitled,注意django 版本为1.11
修改models.py,增加2个表
from django.db import models class Classes(models.Model):
name = models.CharField(max_length=32) class Student(models.Model):
name = models.CharField(max_length=32)
修改urls.py,增加路径
from django.conf.urls import url
from django.contrib import admin
from app01 import views urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^login/$', views.login),
url(r'^student/$', views.student),
url(r'^student/add/$', views.student_add),
]
修改views.py,增加视图函数
from django.shortcuts import render # Create your views here.
def login(request):
"""
用户登陆
:param request:
:return:
"""
if request.method == 'GET':
return render(request,'login.html') def student(request): return render(request,'student.html') def student_add(request):
return render(request, 'student_add.html')
在templates新增文件layout.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div style="height: 48px;background-color: aquamarine"></div>
<div>
<div style="width: 20%;float: left;background-color: #dddddd">
菜单
</div>
<div style="width: 80%;float: left">
{% block content %} {% endblock %}
</div>
</div>
</body>
</html>
student_add.html
{% extends 'layout.html' %} {% block content %}
<h1>添加学生</h1>
{% endblock %}
login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form method="post">
{% csrf_token %} <input type="text" name="user">
<input type="password" name="pwd"> <input type="submit" value="提交"> {{ msg }}
</form>
</body>
</html>
student.html
{% extends 'layout.html' %} {% block content %}
<h1>学生列表</h1>
{% endblock %}
启动djang项目,访问页面
http://127.0.0.1:8000/student/add/
效果如下:
拷贝rbac组件
将上面的rbac文件中,直接copy到项目根目录
清空migrations目录
进入rbac目录中的migrations文件夹,将里面的所有文件删除
注册rbac到app
修改settings.py,注册rbac到app
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app01.apps.App01Config',
'rbac.apps.RbacConfig',
]
数据库迁移
使用2个命令,生成表。注意:定义要带上应用名rbac,否则无法生成表!
python manage.py makemigrations rbac
python manage.py migrate
查看表是否生成了
录入权限信息
创建超级用户
python manage.py createsuperuser
查看admin.py
from django.contrib import admin
from rbac import models class PermissionAdmin(admin.ModelAdmin):
list_display = ['title','url'] # 显示的字段
list_editable = ['url'] # 允许编辑 admin.site.register(models.Permission,PermissionAdmin) admin.site.register(models.Role)
admin.site.register(models.UserInfo)
登录后台,添加数据
添加角色
添加用户
用户登陆做权限和菜单的初始化
修改app01目录下的views.py
from django.shortcuts import render, redirect,HttpResponse
from rbac import models
from rbac.service.init_permission import init_permission def login(request):
"""
用户登陆
:param request:
:return:
"""
if request.method == 'GET':
return render(request,'login.html') # 1. 获取提交的用户名和密码
user = request.POST.get('user')
pwd = request.POST.get('pwd') # 2. 检验用户是否合法
obj = models.UserInfo.objects.filter(name=user,password=pwd).first()
if not obj:
return render(request, 'login.html',{'msg':'用户名或密码错误'})
request.session['user_info'] = {'id': obj.id, 'name': obj.name}
init_permission(request,obj)
return redirect('/student/') def student(request): return render(request,'student.html') def student_add(request):
return render(request, 'student_add.html')
应用中间件进行权限校验
修改settings.py,注册中间件
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.middleware.rbac.RbacMiddleware',
]
设置配置文件
修改settings.py,最后一行添加
########################## 权限相关 #######################
PERMISSION_SESSION_KEY = "permission_list"
MENU_SESSION_KEY = "menu_list"
VALID_URL = [
'^/login/$',
'^/admin/.*',
]
显示动态菜单
引入css
{% load staticfiles %}
<link rel="stylesheet" href="{% static 'rbac/rbac.css' %}" />
调用动态菜单
{% load rbac %}
{% menu request %}
合并
修改app01-->templates-->layout.html
{% load staticfiles %}
{% load rbac %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link href="//netdna.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
<link rel="stylesheet" href="{% static 'rbac/rbac.css' %}" />
{% block css %} {% endblock %}
</head>
<body>
<div style="height: 48px;background-color: aquamarine"></div>
<div>
<div style="width: 20%;float: left;background-color: #dddddd">
{% menu request %}
</div>
<div style="width: 80%;float: left">
{% block content %} {% endblock %}
</div>
</div>
{% block js %} {% endblock %}
</body>
</html>
登录账户
查看页面
总结:
如何在其他系统中应用目前的rbac组件。
a. 拷贝rbac组件 b. 清空migrations目录 c. 注册rbac 到app d. 数据库迁移并录入权限信息 e. 用户登陆做权限和菜单的初始化 init_permission f. 应用中间件进行权限校验 g. 设置配置文件
########################## 权限相关 #######################
PERMISSION_SESSION_KEY = "permission_list"
MENU_SESSION_KEY = "menu_list"
VALID_URL = [
'^/login/$',
'^/admin/.*',
] h. 显示动态菜单
- 引入css
{% load staticfiles %}
<link rel="stylesheet" href="{% static 'rbac/rbac.css' %}" />
- 调用动态菜单 {% load rbac %}
{% menu request %}
合起来:
{% load staticfiles %}
{% load rbac %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="{% static 'rbac/rbac.css' %}" />
{% block css %} {% endblock %}
</head>
<body>
<div style="height: 48px;background-color: aquamarine"></div>
<div>
<div style="width: 20%;float: left;background-color: #dddddd">
{% menu request %}
</div>
<div style="width: 80%;float: left">
{% block content %} {% endblock %}
</div>
</div>
{% block js %} {% endblock %}
</body>
</html>
四、django static文件的引入方式
创建 static文件夹
在django project中创建 static文件夹
配置静态目录
settings.py中配置要在 STATIC_URL = '/static/' 下边
STATICFILES_DIRS = [ os.path.join(BASE_DIR, 'static'), ]
或
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
前端引入
方法一(推荐)
在页面的较上处写:
{% load staticfiles %}
在 link script 等src 写 :
{%static 'xxx.css'%} {%static 'xxx.js'%}
完整代码:
<link rel="stylesheet" href="{%static 'xxx.css'%} "/>
<script src="{%static 'xxx.js'%} "></script>
方式二
在 link script 等src 写
/static/xxx.cs
两者可混用,但不推荐!
推荐使用方式一
查找过程
默认的django程序,会设置STATIC_URL
Django 默认会在各app下的static文件夹中找文件。注意有先后顺序,找到了就不再继续找了!、
那在模版中使用这些静态文件时,使用
{%static 'sample.css'%}
{%static 'xxx.js'%}
Django在运行时会自动将这些文件映射到STATIC_URL所给定的值下。也就是如,如果STATIC_URL = '/static/',那么在运行时,上边模版中的样例中的url会被替换为/static/css/sample.css
注意事项
1.其实将静态文件路径硬编码在模版中也可以正常运行,如使用href="/static/css/sample.css",前提是配置好了STATIC_URL和STATIC_DIRS。但并不推荐这么做,因为如果后来静态资源的位置发生了迁移,如使用独立服务器或者使用CDN,就要修改一大堆URL。而使用推荐的方法可以避免这个庞大的工作量,最多只需要修改STATIC_URL即可。
2.如果css文件中也使用了静态文件如css背景,则按照相对路径使用即可,因为浏览器解析css是会自动按照相对路径寻找到正确的URL。
综上所述:默认会配置STATIC_URL,在模板中推荐使用{%static 'sample.css'%} 这种方式!
在这里还需要强调的是,在开发阶段,Django使用的是内建的一个静态文件服务器,虽然在生产环境中也可以使用,但是它既不稳定也不安全。更好的方式是使用提供web服务的服务器如apache来服务静态文件。这需要你首先上传代码到服务器,然后运行collectstatic命令:python manage.py collectstatic 然后配置web服务器来为静态文件服务,如对Apache2进行配置。
作业:
如何实现动态生成二级菜单
思路
一级菜单,在数据库加字段;
二级菜单,在权限表中,
表设计如下:
class Menu(models.Model):
"""
菜单
"""
title = models.CharField(verbose_name='菜单', max_length=32)
icon = models.CharField(verbose_name='图标', max_length=32) def __str__(self):
return self.title class Permission(models.Model):
"""
权限表
"""
title = models.CharField(verbose_name='标题', max_length=32)
url = models.CharField(verbose_name='含正则的URL', max_length=128) menu = models.ForeignKey(verbose_name='菜单', to='Menu', null=True, blank=True, help_text='null表示非菜单') def __str__(self):
return self.title
完整models.py如下:
from django.db import models class Menu(models.Model):
"""
菜单
"""
title = models.CharField(verbose_name='菜单', max_length=32)
icon = models.CharField(verbose_name='图标', max_length=32) def __str__(self):
return self.title class Permission(models.Model):
"""
权限表
"""
title = models.CharField(verbose_name='标题', max_length=32)
url = models.CharField(verbose_name='含正则的URL', max_length=128) menu = models.ForeignKey(verbose_name='菜单', to='Menu', null=True, blank=True, help_text='null表示非菜单')
def __str__(self):
return self.title class Role(models.Model):
"""
角色
"""
title = models.CharField(verbose_name='角色名称', max_length=32)
permissions = models.ManyToManyField(verbose_name='拥有的所有权限', to='Permission', blank=True) def __str__(self):
return self.title class UserInfo(models.Model):
"""
用户表
"""
name = models.CharField(verbose_name='用户名', max_length=32)
password = models.CharField(verbose_name='密码', max_length=64)
email = models.CharField(verbose_name='邮箱', max_length=32)
roles = models.ManyToManyField(verbose_name='拥有的所有角色', to='Role', blank=True) def __str__(self):
return self.name
python 全栈开发,Day108(客户管理之权限控制,客户管理之动态"一级"菜单,其他应用使用rbac组件,django static文件的引入方式)的更多相关文章
- python 全栈开发,Day107(CRM初始,权限组件之权限控制,权限系统表设计)
一.CRM初始 CRM,客户关系管理系统(Customer Relationship Management).企业用CRM技术来管理与客户之间的关系,以求提升企业成功的管理方式,其目的是协助企业管理销 ...
- python 全栈开发,Day129(玩具开机提示语,为多个玩具发送点播,聊天界面,app录音,app与服务器端文件传输,简单的对话)
一.玩具开机提示语 先下载github代码,下面的操作,都是基于这个版本来的! https://github.com/987334176/Intelligent_toy/archive/v1.2.zi ...
- Python全栈开发【面向对象】
Python全栈开发[面向对象] 本节内容: 三大编程范式 面向对象设计与面向对象编程 类和对象 静态属性.类方法.静态方法 类组合 继承 多态 封装 三大编程范式 三大编程范式: 1.面向过程编程 ...
- python 全栈开发之路 day1
python 全栈开发之路 day1 本节内容 计算机发展介绍 计算机硬件组成 计算机基本原理 计算机 计算机(computer)俗称电脑,是一种用于高速计算的电子计算机器,可以进行数值计算,又可 ...
- python全栈开发中级班全程笔记(第二模块、第四章)(常用模块导入)
python全栈开发笔记第二模块 第四章 :常用模块(第二部分) 一.os 模块的 详解 1.os.getcwd() :得到当前工作目录,即当前python解释器所在目录路径 impor ...
- Python全栈开发【面向对象进阶】
Python全栈开发[面向对象进阶] 本节内容: isinstance(obj,cls)和issubclass(sub,super) 反射 __setattr__,__delattr__,__geta ...
- Python全栈开发【模块】
Python全栈开发[模块] 本节内容: 模块介绍 time random os sys json & picle shelve XML hashlib ConfigParser loggin ...
- Python全栈开发【基础四】
Python全栈开发[基础四] 本节内容: 匿名函数(lambda) 函数式编程(map,filter,reduce) 文件处理 迭代器 三元表达式 列表解析与生成器表达式 生成器 匿名函数 lamb ...
- Python全栈开发【基础三】
Python全栈开发[基础三] 本节内容: 函数(全局与局部变量) 递归 内置函数 函数 一.定义和使用 函数最重要的是减少代码的重用性和增强代码可读性 def 函数名(参数): ... 函数体 . ...
随机推荐
- Docker简介以及安装
Docker简介以及安装 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.什么是容器 1.一种虚拟化方案 与传统的虚拟机不同,传统的虚拟机是通过中间层将一台或多台独立的机器虚拟运 ...
- .net MVC使用NPOI读取Excel模板,再写入数据
NPOI其实已经介绍的差不多了,再贴一个方便以后复制粘贴. 亮点其实是 Server.MapPath 这个东西,可以找到MVC工程下的文件夹,找路径还是很方便的. /// <summary> ...
- dubbo注册服务和消费服务---入门篇
本文介绍如何用dubbo+zk来实现一个注册服务 + 消费服务的入门小demo 需要环境:zk服务器 两个maven项目,一个负责提供服务,一个负责消费服务. dubbo-service 服务端 po ...
- SQL记录-解锁和dbms_job操作
创建JOB create or replace procedure proc_auto_exec_job as begin declare job number; BEGIN dbms_job.sub ...
- mySQL数值类型的取值范围
如下图,int最大为2145483647,手机号码应该用bigint
- HTML5的 input:file上传 以及 类型控制
以HTML5的文件上传API 如下demo代码在.html文件打开即可: !DOCTYPE html> <html lang="zh_cn"> <head& ...
- HDU 2191 - 单调队列优化多重背包
题目: 传送门呀传送门~ Problem Description 急!灾区的食物依然短缺! 为了挽救灾区同胞的生命,心系灾区同胞的你准备自己采购一些粮食支援灾区,现在假设你一共有资金n元,而市场有m种 ...
- strstr函数字符串匹配问题
题目链接:http://acm.sdut.edu.cn/onlinejudge2/index.php/Home/Index/problemdetail/pid/2772.html AC代码: #inc ...
- Servlet3.0的注解自定义原生Servlet实战
Servlet3.0的注解自定义原生Servlet实战 讲解:使用 Servlet3.0的注解自定义原生Servlet和Listener 自定义原生Servlet package net.xdclas ...
- oaracel 函数_行转列
wm_concat(varchar2) 组函数