1. CRM项目回顾(纸笔黑画流程图): 1. 项目大体情况 客户管理系统/物料管理系统/库存管理系统/会员管理系统 职责:让公司销售管理我们的客户,会有持续的跟进记录,成单,关单,根据关单算销售提成 2.项目架构: Django+MySQL+Bootstrap 3. 关键技术点 1. auth模块--做用户认证注册 2. form和ModelForm做数据的增删改查 3. admin做管理界面(后台管理员) 4. formset 4. 业务流程 1. 网络咨询师工作-->录入客户信息-->加入公户 2. 销售工作: 1. --> 把公户转为私户 2. --> 记录沟通记录 --> 放回公户(搞不定的客户) 3. --> (搞定的客户)报名--指定班级 4. --> 缴费 3. 班主任工作: 1. --> 创建班级 2. --> 课程记录(记录课程记录:每天的上课记录day01~day120) 3. --> 学习记录(记录每位同学的考勤和作业成绩) 5. 这个项目你收获最大的是什么?/你遇到的最大的难题是什么? 1. 分页 --> 基本封装 2. 建表 --> 数据库三大范式 3. 类继承的应用: 1. 封装固定的功能到父类中 1. BootstrapBaseForm 2. auth模块的Abstractser 4. 反射应用: 1. settings.py中定制配置项(优雅的获取配置文件(让代码更健壮)--用反射去取settings中配置项) if hasattr(settings, 'WHITE_URL'):#代码中加判断settings模块中有无white_url配置项 WHITE_URL = getattr(settings, 'WHITE_URL')#有就取 else:#无则设一默认值 WHITE_URL = [] 2. 批量操作: 1. 为减少代码的冗余:用反射判断我当前视图cbv中有无以下划线开头的方法,就是通过反射实现这些私有方法 _to_public() _to_private() _multi_init() 5. 数据库ORM: 1. bulk_create(之前创建时销售人员多时老是感觉项目运行有点慢,后来就做代码的重构/审查发现把两嵌套的for循环做初始化时创建一个提交一个,所以系统大量时间都浪费在建立与断开数据库连接上),后来就用orm的bulk_create方法-->专门用来提供批量创建数据时的效率--多个数据一起提交 2. 事务操作和行级锁 --> 当时存在多个销售争抢同一个客户--怎么保证先到先得数据-->加行级锁(某条记录我更新完后别人才能对它操作) 6. QueryDict 1. 学会看源码:发现querydict对象有mutable参数 --> 我配置mutable=True时才能修改 2. 还看Form源码的is_valid()-->发现在内部可以定义自己的局部/全局的Hook(勾子)方法--如确认密码校验时就用到全局勾子 2. 权限系统介绍: 1. 分析问题 1. 什么是权限? 同一套系统不同的用户可以有不同的权限 2. 为什么需要权限? 不用岗位的人能做的事情不一样的 3. 如何实现权限控制? RBAC(专业术语--基于角色的权限控制系统--不同用户不同功能) 2. 写代码 1. 建表 录入信息 1. 安装xlrd模块pip install xlrd,专门用来操作excel文件(python操作excel文件的模块) 2. 如何在Django项目实现权限控制 1. 访问某个URL的时候,要判断这个URL在不在我的权限列表里面 2. 在中间件的process_request方法中对请求做权限判断 1. 先拿到访问的URL --> request.path_info 2. 获取当前用户的权限有哪些(哪些url) 1. 必须登录 --> 登录之后才能拿到用户,有了用户才能查询该用户的权限有哪些
(怎么查权限?--通过用户查角色,通过角色查对应权限--跨两表太慢,所以提前把用户信息存到session中下次用户来时直接session查不用再连表了--request.session就可获取,django默认把session存到django_session表中,一般redis,但这里我直接session中) 1. 把当前登录的用户的权限信息保存到session数据中 2. 每次请求来 从当前请求的session数据中拿到该用户的权限信息 3. 中间件中判断是否有权限 1. 不能简单的用in操作 2. 给权限判断设置白名单
一.权限系统介绍


打开我的luffy_permission项目(效果如图上)这里是我把权限组件在此项目中写,新建app做权限的rbac--python manage.py startapp rbac.
在命令行创建的app一定要注册下告诉django项目,settings.py中--'rbac.apps.RbacConfig',
(1)models.py中建三表:
: 权限表中存的是权限--其实存的就是url(权限不一样即访问的url不一样)---存web项目的url
. 上图中可看出,用户和角色有多对多的关系,我把多对多建立在用户表(因为查询权限的时候有用户查权限查的较多--所以多对多字段设在查询较多的表中)。
角色与权限也是多对多关系,把多对多放角色表中,因为角色查的多。(并改成中文名)
from django.db import models #权限表 class Permission(models.Model): title = models.CharField(max_length=32) url = models.CharField(max_length=32) def __str__(self): return self.title class Meta: verbose_name = '权限' verbose_name_plural = verbose_name #用户表 class UserInfo(models.Model): username = models.CharField(max_length=32) password = models.CharField(max_length=32) roles = models.ManyToManyField(to='Role') class Meta: verbose_name = '用户' verbose_name_plural = verbose_name #角色表 class Role(models.Model): title = models.CharField(max_length=32) permissions = models.ManyToManyField(to='Permission') class Meta: verbose_name = '角色' verbose_name_plural = verbose_name
然后建表--python manage.py makemigrations-python manage.py migrate
(2)rbac/admin.py:录入数据---用django 的admin:
先在rbac中admin.py中把上述几个表注册
然后创建超级用户:python manage.py createsuperuser -----lizhihua lzh12345
http://127.0.0.1:8007/admin/登录后如下图中在权限中分别录入如下数据:


添加完后,权限页面中显示的是Permission object而没有刚才录入的内容,所以要在models.py中给权限表设置__str__方法返回字符串--结果如上图3中所示.
且我想在权限页面中展示权限同时展示它对应的url--在adimn中自定制权限表的类list_display中加入字段即可效果如上图4中所示.
且上图4中是展示url了但是不支持修改,---admin中自定制的类中list_editable中加入字段即可.效果如上图5所示.
from django.contrib import admin from rbac.models import UserInfo,Permission,Role #自定制一权限类的adimn--并把这类传给models就行 class PermissionAdmin(admin.ModelAdmin):#用这个类去控制权限表在admin管理后台的显示效果 list_display = ['title','url'] #就是在adimn页面上展示的字段有 list_editable = ['url'] #告诉它哪个字段可支持编辑 admin.site.register(UserInfo) admin.site.register(Permission,PermissionAdmin)#把定制类传进来 admin.site.register(Role)
二.创建表和录入信息:

1.录入信息
(1)如图1中,给各角色录入对应操作权限(Boss所有,经理无删除,销售无编辑,)
(2)如上图2中分别建4个用户及对应角色.
2.把权限系统应用入luffey_pression项目中.

上图中如何实现权限控制?---就是如你点击客户管理中的添加客户时它是跳转到/customer/add/这个url中的,所以就是判断这个url在不在某用户的权限列表中,在的话可以访问此url,不在则拒绝。这个逻辑相当于要给前端每个按钮绑定js事件,那页面上是js,后端没法知道对应权限是什么,若是发ajax请求那麻烦(点一次按钮发一次),
如下图这是一个url请求的走法:

判断用户有无权限访问某url,就加到上图中间件部分中,且中间件的五个方法中的process_request方法中(请求来了就做判断)
三.登录之后保存权限信息:
(1)luffy_permission/urls.py:
from django.conf.urls import url,include from django.contrib import admin from rbac import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^login/$', views.login), url(r'^logout/$', views.logout), url(r'^', include('web.urls')), ]
(2)rbac/views.py中:这里我没用auth模块
from django.shortcuts import render from rbac.models import UserInfo from django.shortcuts import render, redirect, HttpResponse from rbac.models import UserInfo from django.conf import settings def login(request): error_msg = '' #默认空则不展示 if request.method == 'POST':#登录时输入用户名密码点提交 username = request.POST.get('username') pwd = request.POST.get('password') #注意filter得到的是query_set,user_obj是一对象,可以点属性,而query_set不能点,所以一定要加first user_obj = UserInfo.objects.filter(username=username,password=pwd).first()#拿到用户 if user_obj:#登录成功 #1.将当前登录用户的权限信息查询出来--且排除没权限的角色 permission_queryset = user_obj.roles.all().filter(permissions__isnull=True).values_list('permissions__url') #是query_set类型所以不能直接点来拿属性,可通过它的values方法来拿(字段双下划线就跨表拿了),这里用values_list列表(里边每一个元素是一小元组) permission_list = [i[0] for i in permission_queryset]#前边的i是列表中每一个元组拿出来,而我要存的不是元组是元组中第一个元素即(/customer/add/)这种url,所以i[0] # 2.将权限信息保存到session数据 request.session['permission_url'] = permission_list return redirect('/customer/list/') else: error_msg = '用户名或密码错误' return render(request,'login.html',{'error_msg':error_msg}) def logout(request): request.session.flush() return redirect('/login/')
(3)rbac/templates/login.html:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>登录</title> </head> <body> <form action="" method="post"> {% csrf_token %} <input type="text" name="username"> <input type="password" name="password"> <input type="submit"> <p>{{ error_msg }}</p> </form> </body> </html>

四.中间件中实现权限校验:
(1)rbac/middleware.py:
写一类必须继承。
且得把此中间件在settings.py中注册'rbac.middleware.RBACMiddleware',
且目当前访问的url是否在白名单中,建议把它写在settins.py中,因为后期会做动态修改
# 权限组件的相关配置
WHITE_URLS = [
'/login/',
'/logout/',
'/reg/',
'/admin/.*',
]
PERMISSION_SESSION_KEY = 'permission_url'
#把session的key也做成一配置项放settings.py,因为我后面在很多地方都用到它(不同模块中可能用到的变量通常放到一公认配置文件中),PERMISSION_SESSION_KEY = 'permission_url'
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse
from django.conf import settings
import re
class RBACMiddleware(MiddlewareMixin):
def process_request(self, request):#传当前请求的request
# 1. 获取当前请求的URL
current_url = request.path_info
# 判断settings模块中是否配置了WHITE_URLS
while_urls = getattr(settings, 'WHITE_URLS', [])
# 2.判断当前访问的url是否在白名单中
for url in while_urls:
if re.match(r'^{}$'.format(url), current_url):
return # 如果是白名单的Url则直接放行
# 判断当前这次请求的URL在不在权限列表里面
# 3. 当前登陆的这个人他的权限列表是什么
key = getattr(settings, 'PERMISSION_SESSION_KEY', 'permission_list')
permission_list = request.session.get(key, [])
# 4. 因为Django URL存在模糊匹配,所以校验权限的时候也要用正则去匹配
for pattern in permission_list:
if re.match('^{}$'.format(pattern), current_url):#match是从头开始匹配,search是只要包含就可以,findall是从头开始找到所有匹配项
#匹配上说明有权限
return None
else:
return HttpResponse('没有权限')
(2)rbac/views.py中:
from django.shortcuts import render, redirect, HttpResponse
from rbac.models import UserInfo
from django.conf import settings
# Create your views here.
def login(request):
error_msg = '' #默认空则不展示
if request.method == 'POST':#登录时输入用户名密码点提交
username = request.POST.get('username')
pwd = request.POST.get('password')
#注意filter得到的是query_set,user_obj是一对象,可以点属性,而query_set不能点,所以一定要加first
user_obj = UserInfo.objects.filter(username=username, password=pwd).first()
if user_obj:
# 登陆成功
# 1. 将当前登录用户的权限信息查询出来
# user_obj.roles.all() # QuerySet
#前边的i是列表中每一个元组拿出来,而我要存的不是元组是元组中第一个元素即(/customer/add/)这种url,所以i[0]
permission_queryset = user_obj.roles.all().filter(permissions__isnull=False).values_list('permissions__url')
permission_list = [i[0] for i in permission_queryset]
# 2. 将权限信息保存到session数据中
key = getattr(settings, 'PERMISSION_SESSION_KEY', 'permission_list')
request.session[key] = permission_list
return redirect('/customer/list/')
else:
error_msg = '用户名或密码错误'
return render(request, 'login.html', {'error_msg': error_msg})
def logout(request):
request.session.flush()
return redirect('/login/')
效果如下图:当图1中用户王帅(角色是boss所以可操作图2中的所有增删改查),但换夏雨(实习生角色)用户登录时登录不上去。

浙公网安备 33010602011771号