day01

day 01

models文件

  用户和角色多对多的原因在于,可能存在临时项目。某个用户除了正常角色,还有可能是安全小组类似的这样的角色。

from django.db import models

# Create your models here.
class UserInfo(models.Model):
    name=models.CharField(max_length=32,verbose_name='用户名')
    pwd=models.CharField(max_length=32,verbose_name='密码')
    email=models.CharField(max_length=32,verbose_name='邮箱')
    roles=models.ManyToManyField(to='Role')
    def __str__(self):
        return self.name

class Role(models.Model):
    name=models.CharField(max_length=32,verbose_name='角色名')
    permissions=models.ManyToManyField(to='Permission')
    def __str__(self):
        return self.name



class Permission(models.Model):
    name=models.CharField(max_length=32,verbose_name='权限名')
    url=models.CharField(max_length=200,verbose_name='网址',default=None)
    def __str__(self):
        return self.name

PS:考虑到有可能有这种情况。新增了一个role,但是暂时没有权限。so,将上述代码修正如下。 roles=.......,blank=True)

from django.db import models

# Create your models here.
class UserInfo(models.Model):
    name=models.CharField(max_length=32,verbose_name='用户名')
    pwd=models.CharField(max_length=32,verbose_name='密码')
    email=models.CharField(max_length=32,verbose_name='邮箱')
    roles=models.ManyToManyField(to='Role')
    def __str__(self):
        return self.name

class Role(models.Model):
    name=models.CharField(max_length=32,verbose_name='角色名')
    permissions=models.ManyToManyField(to='Permission',blank=True)
    def __str__(self):
        return self.name



class Permission(models.Model):
    name=models.CharField(max_length=32,verbose_name='权限名')
    url=models.CharField(max_length=200,verbose_name='网址',default=None)
    def __str__(self):
        return self.name

  

用户 --->角色---->权限,正向查询比价方便。所以,多对多的关系,如上图所示。

views文件

Func1:

用户登录后,后台视图函数通过request.POST.get方法 拿到前端传来的数据,对数据库内的数据进行过滤。

在此之前,把models里的所有模型导入到views文件中。

from rbac.models import *

def login(request):
    if request.method=='POST':
        username=request.POST.get('username')
        password=request.POST.get('password')
        user=UserInfo.objects.filter(name=username,pwd=password).first()

  

  

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Func2:

如果验证通过,查询此人的权限。UserInfo -->Role -->  Permissions

补充:

values

 

values(*fields)

返回一个ValuesQuerySet —— QuerySet 的一个子类,迭代时返回字典而不是模型实例对象。

每个字典表示一个对象,键对应于模型对象的属性名称

        if user:
            permission_list1=user.roles.all()
            for i in permission_list1:
                print(i,type(i))
            permission_list4 = user.roles.all().values()
            for i in permission_list4:
                print(i, type(i))
            permission_list2 = user.roles.all().values('name')
            for i in permission_list2:
                print(i,type(i))
            permission_list3=user.roles.all().values('name','permissions__name','permissions__url')

            print(permission_list1,type(permission_list1))
            print(permission_list2,type(permission_list2))
            print(permission_list3,type(permission_list3))
            print(permission_list4,type(permission_list4))

输出:

材料员 <class 'rbac.models.Role'>
{'id': 4, 'name': '材料员'} <class 'dict'>
{'name': '材料员'} <class 'dict'>
<QuerySet [<Role: 材料员>]> <class 'django.db.models.query.QuerySet'>
<QuerySet [{'name': '材料员'}]> <class 'django.db.models.query.QuerySet'>
<QuerySet [{'name': '材料员', 'permissions__name': '查询订单', 'permissions__url': '/order/select/'}, {'name': '材料员', 'permissions__name': '查询用户', 'permissions__url': '/user/select/'}]> <class 'django.db.models.query.QuerySet'>
<QuerySet [{'id': 4, 'name': '材料员'}]> <class 'django.db.models.query.QuerySet'>
{'name': '材料员', 'permissions__name': '查询订单', 'permissions__url': '/order/select/'}
{'name': '材料员', 'permissions__name': '查询用户', 'permissions__url': '/user/select/'}

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

  Django 提供一种强大而又直观的方式来“处理”查询中的关联关系,它在后台自动帮你处理JOIN。 若要跨越关联关系,只需使用关联的模型字段的名称,并使用双下划线分隔,直至你想要的字段.

from rbac.models import *

def login(request):
    if request.method=='POST':
        username=request.POST.get('username')
        password=request.POST.get('password')
        user=UserInfo.objects.filter(name=username,pwd=password).first()
        # print(user,type(user))
        if user:
            permission_list=user.roles.all().values('name','permissions__name','permissions__url')
            for item in permission_list:
                print(item)
            return HttpResponse('登录成功!')

    return render(request,'login.html')

输出:

{'name': '材料员', 'permissions__name': '查询订单', 'permissions__url': '/order/select/'}
{'name': '材料员', 'permissions__name': '查询用户', 'permissions__url': '/user/select/'}

改进:

可能一个人都多个角色,角色拥有的权限可能有交集。基于这一点考虑,对以上代码进行修正。去掉values()内的name字段。因为这个name字段是roles对象的name,在这里并没有用处,我们需要的是 权限名。

from rbac.models import *

def login(request):
    if request.method=='POST':
        username=request.POST.get('username')
        password=request.POST.get('password')
        user=UserInfo.objects.filter(name=username,pwd=password).first()
        # print(user,type(user))
        if user:
            permission_list=user.roles.all().values('permissions__name','permissions__url').distinct()
            for item in permission_list:
                print(item)
            return HttpResponse('登录成功!')

    return render(request,'login.html')

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

实现用户登录成功后,将其权限加入到session中。请求 不同页面,根据用户权限不同,在页面中显示不同的结果。

为什么加入session中,当未登录的用户 想要查看/user/select/,这是肯定不行的。通过session判断当前用户的权限。同时,减轻了数据库的压力,不必一次一次的查询数据库。

若是用户的权限改了呢?很简单,提示用户重新登录。这是普遍的做法。

def userselect(request):
    '''
    根据有无权限,
    显示所有的用户信息
    :param request:
    :return:
    '''
    if request.session.get('url_list'):
        url_list=request.session.get('url_list')
        flag = False
        for url in url_list:
            if re.match(url,request.path_info):
                flag=True
                break
        if flag:
            users=UserInfo.objects.all()
            return render(request,'userselect.html',{'users':users})
        else:
            return HttpResponse('无权访问')
    else:
        return redirect('/login/')

其它的也顺理成章喽

  

def orderselect(request):
    '''
        根据有无权限,
        显示所有的订单信息
        :param request:
        :return:
        '''
    if request.session.get('url_list'):
        url_list = request.session.get('url_list')
        flag = False
        for url in url_list:
            if re.match(url, request.path_info):
                flag = True
                break
        if flag:
            orders= Order.objects.all()
            return render(request, 'orderselect.html', {'orders':orders})
        else:
            return HttpResponse('无权访问')
    else:
        return redirect('/login/')

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

上一步有没有优化的地方呢?

感受到了

在rbac这个项目中,所有的视图函数,都需要先判定下当前用户有无访问此页面的权限。也就是说每个视图函数都需要用同一段代码,实现用一个功能。

方法有两个:装饰器,中间件。在这里,中间件是更好的选择。

来吧,写一个中间件。

补充知识:

process_request(request)

request是一个HttpRequest 对象。

在Django决定执行哪个视图之前,process_request()会在每个请求上调用。

它应该返回一个None 或一个HttpResponse对象。如果返回None,Django会继续处理这个请求,执行其它process_request()中间件,然后process_view()中间件,最后是对应的视图。如果它返回一个HttpResponse对象,Django 就不用再去调用其它的request、view 或exception 中间件,或对应的视图;它将对HttpResponse 运用响应阶段的中间件,并返回结果。

 

所以,在中间件中,有问题返回Httpresponse对象,不在调用其他中间件了。如果没问题,则返回None。

同时,在视图函数中,需要对 条件判断的进行修正,将 会出问题的判断放在 if 语句中。

views文件中

def userselect(request):
    '''
    根据有无权限,
    显示所有的用户信息
    :param request:
    :return:
    '''

    url_list=request.session.get('url_list')
    flag = False
    for url in url_list:
        if re.match(url,request.path_info):
            flag=True
            break
    if not flag:
        return HttpResponse('无权访问')
    users = UserInfo.objects.all()
    return render(request, 'userselect.html', {'users': users})

然后将其整体移植到自定义的中间件中。将自定义的 中间件写入设置。

最终如下:

中间件:

from django.utils.deprecation import MiddlewareMixin
class RbacMiddleware(MiddlewareMixin):
    def process_request(self,requst):
        url_list = request.session.get('url_list')
        flag = False
        for url in url_list:
            if re.match(url, request.path_info):
                flag = True
                break
        if not flag:
            return HttpResponse('无权访问')
        return None

视图函数:

def userselect(request):
    '''
    根据有无权限,
    显示所有的用户信息
    :param request:
    :return:
    '''
    users = UserInfo.objects.all()
    return render(request, 'userselect.html', {'users': users})

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.middlewares.rbac.RbacMiddleware',
]

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

小改动

统一加上了中间件,但是有一点小问题。当登录login页面是,也是返回无权访问。需要经login设为白名单。

在中间件中加一行代码即可。

  

from django.shortcuts import render,redirect,HttpResponse
import re
from django.utils.deprecation import MiddlewareMixin
class RbacMiddleware(MiddlewareMixin):
    def process_request(self,request):
        if request.path_info=='/login/':
            return None
        if request.path_info=='/admin/':
            return None
        url_list = request.session.get('url_list')
        flag = False
        for url in url_list:
            if re.match(url, request.path_info):
                flag = True
                break
        if not flag:
            return HttpResponse('无权访问')
        return None

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

微调使文件更具有可读性

  在解决中间件问题后,用户登录成功后,将权限放入其session中,经过这样一个初始化操作。因为有可能开发另外一个系统,也会经历这个初始化操作。所以。选择将其拿出来写作一个函数,写入一个单独的文件中service/init_permission.py。

  

views文件:

import re
# Create your views here.
from rbac.models import *
from rbac.service.init_permission import init_permission
def login(request):
    if request.method=='POST':
        username=request.POST.get('username')
        password=request.POST.get('password')
        user=UserInfo.objects.filter(name=username,pwd=password).first()
        # print(user,type(user))
        if user:
            init_permission(user,request)
            return HttpResponse('登录成功')

    return render(request,'login.html')

service/init_permission

def init_permission(user,request):
    # permission_list = user.roles.all().values('permissions__name', 'permissions__url').distinct()
    permission_list = user.roles.filter(permissions__name__isnull=False).values('permissions__name', 'permissions__url').distinct()

    url_list = []
    for item in permission_list:
        url_list.append(item.get('permissions__url'))
    request.session['url_list'] = url_list

补充一个小知识:

isnull

值为 True 或 False, 相当于 SQL语句IS NULLIS NOT NULL.

例:

 

Entry.objects.filter(pub_date__isnull=True)

SQL等效:

SELECT ... WHERE pub_date IS NULL;

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

代码需要润色的几点

  1 白名单可能有很多,将这些白名单写在settings文件中,从settings文件中读取,更合理。

  注释的是更正之前的代码。

from django.shortcuts import render,redirect,HttpResponse
import re
from django.utils.deprecation import MiddlewareMixin
from permission import settings
class RbacMiddleware(MiddlewareMixin):
    def process_request(self,request):
        #1 获取白名单
        permission_valid_url=settings.PERMISSION_VALID_URL
        for url in permission_valid_url:
            if re.match(url,request.path_info):
                return None
        # if request.path_info=='/login/':
        #     return None
        # if request.path_info=='/admin/':
        #     return None
        #2 获取权限
        url_list = request.session.get('url_list')
        #3 对用户请求的url进行匹配
        flag = False
        for url in url_list:
            if re.match(url, request.path_info):
                flag = True
                break
        if not flag:
            return HttpResponse('无权访问')
        return None

  2 request.session.get('url_list'),可能会在这个项目的很多地方用到,对于这种可能会用到很多次,可以将其写在配置文件中。

  在此项目中,两处需要修改。

  中间件中:

from django.shortcuts import render,redirect,HttpResponse
import re
from django.utils.deprecation import MiddlewareMixin
from django.conf import settings
class RbacMiddleware(MiddlewareMixin):
    def process_request(self,request):
        #1 获取白名单
        permission_valid_url=settings.PERMISSION_VALID_URL
        for url in permission_valid_url:
            if re.match(url,request.path_info):
                return None
        # if request.path_info=='/login/':
        #     return None
        # if request.path_info=='/admin/':
        #     return None
        #2 获取权限
        url_list = request.session.get(settings.PERMISSION_SESSION_KEY)
        # url_list = request.session.get('url_list')
        if not  url_list:
            return HttpResponse('未能读取到该用户的信息')
        #3 对用户请求的url进行匹配
        flag = False
        for url in url_list:
            if re.match(url, request.path_info):
                flag = True
                break
        if not flag:
            return HttpResponse('无权访问')
        return None

  init_permission初始化文件中:

from django.conf import settings
def init_permission(user,request):
    # permission_list = user.roles.all().values('permissions__name', 'permissions__url').distinct()
    permission_list = user.roles.filter(permissions__name__isnull=False).values('permissions__name', 'permissions__url').distinct()

    url_list = []
    for item in permission_list:
        url_list.append(item.get('permissions__url'))
    request.session[settings.PERMISSION_SESSION_KEY] = url_list
    # request.session['url_list'] = url_list

  settings文件中添加的两条配置:

PERMISSION_VALID_URL=[
    '/login/',
    '/admin/.*',

]
PERMISSION_SESSION_KEY='url_list'

  最后:目录结构如下

  

原文地址:https://www.cnblogs.com/654321cc/p/8318911.html