django自定义后端过滤

news2024/12/31 7:31:48

DRF自带的过滤

  1. 第一个 DjangoFilterBackend 是需要安装三方库见[搜索:多字段筛选]
  2. 两外两个是安装注册了rest_framework就有。
    在这里插入图片描述

如上图,只要配置了三个箭头所指的方向,就能使用。

第一个单字段过滤

用户视图集中加上filterset_fields 后,后端搜索过滤就生效了

特点

  • 是准确匹配,如搜王老五,能搜索来,搜老五,是搜不出来的
    在这里插入图片描述

第二个关键字过滤

配置SearchFilter, 即可实现。就不举例了。

  # 后端过滤、搜索与排序
  filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]  

现在已经知道使用djangRF 视图集定义的接口,比如列表接口 可以加参数 ?search=xxx , 进行数据的过滤。

第三个排序

也不举例了,见 添加链接描述

这三个的原理

简单总结得说:

  • 就是DjangoFilterBackend, SearchFilter, OrderingFilter三个类中有一个filter_queryset的方法, 传入一个querySet,返回过滤后的querySet。

不清楚得看下面源码说明, 接口都使用了 filter_queryset 方法。
在这里插入图片描述

而filter_queryset 是GenericAPIView 类中的代码如下:

(大致逻辑:给一个querySet,返回出过滤后的 querySet)


    def filter_queryset(self, queryset):
        """
        Given a queryset, filter it with whichever filter backend is in use.

        You are unlikely to want to override this method, although you may need
        to call it either from a list view, or from a custom `get_object`
        method if you want to apply the configured filtering backend to the
        default queryset.
        """
        for backend in list(self.filter_backends):
            queryset = backend().filter_queryset(self.request, queryset, self)
        return queryset

结合已有知识可知,
filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]

继续观察上面代码的写法(如下图)
在这里插入图片描述

每一项backend后面都.filter_queryset ,
说明SearchFilter 或者 OrderingFilter 和DjangoFilterBackend 这是三个类,且里面都有filter_queryset 的方法
看源码,果然是 ,它们的filter_queryset 里面就有过滤逻辑:
在这里插入图片描述

自定义过滤

针对第一个单字段过滤,修改成模糊匹配

找个地方写py文件,如在utils包下,新建一个取名为filters.py 的文件

import django_filters

from system.models import User

class UsersManageFilter(django_filters.rest_framework.FilterSet):
    """
    用户管理 简单过滤器
    URL格式:http://127.0.0.1:8100/?start_time=2022-10-02 08:00:00&end_time=2023-12-31 23:59:59
    """
    # 1.创建时间从
    start_time = django_filters.DateTimeFilter(field_name='create_datetime', lookup_expr='gte')
    # 2.创建时间到
    end_time = django_filters.DateTimeFilter(field_name='create_datetime', lookup_expr='lte')
    # 3.用户名-模糊搜索
    username = django_filters.CharFilter(field_name='username', lookup_expr='icontains')
    # 4.姓名号码-模糊搜索
    realname = django_filters.CharFilter(field_name='mobile', lookup_expr='icontains')

    class Meta:
        model = User
        fields = []

在到视图集下,写 filterset_class即可生效。
在这里插入图片描述
除了改成模糊支持模糊搜索,还可以比如根据某个字段的时间范围来过滤等等

具体可以搜索 :django_filters如何使用

根据原理自己写一个过滤类,比如实现数据过滤

同样在filters.py中, 自定义一个类,需要继承BaseFilterBackend,写一个filter_queryset方法即可、 里面就可以写根据用户角色,角色的权限来返回不同的数据了
在这里插入图片描述

同时,视图集中,在filter_backends中,要加上这个类。
在这里插入图片描述

这里先写一个简单易懂的例子,试试

  • 根据登录用户,超级管理员就返回所有的数据
  • 非超级管理员,假设只能看见自己创建的数据
from rest_framework.filters import BaseFilterBackend
class DataPermissionFilter(BaseFilterBackend):
    def filter_queryset(self, request, queryset, view):
        # 1.超级管理员,返回所有数据
        if request.user.is_superuser == 0:
            # 2.非超级管理管理,还要进行判断
            # 获取用户id
            user_id = getattr(request.user, 'id', None)
            # 返回创建者 登录人自己创建的数据
            return queryset.filter(creator=user_id)
        else:
            return queryset

验证:为非超级管理员时
在这里插入图片描述
在这里插入图片描述

为超级管理员时,就返回的全部
在这里插入图片描述

  1. 以上是一个简单的例子。是根据queryset中的创建人来的,首先要满足你所查询的querySet是拥有创建人这个字段的。

  2. 复杂点的逻辑一,可以是根据用户拥有的角色,角色又拥有哪些数据,
    假如
    A角色关联的是 1公司,2公司数据,
    B角色关联的是 2公司,3公司数据
    当查询某种数据时,对应的模型也就需要有公司字段。

3.复杂点的逻辑二, 可以是根据用户拥有的角色,角色又拥有部门,
要查询的数据也拥有部门,实现了数据权限
角色模型如下:
在这里插入图片描述
业务模型:
如这个Project项目模型,它是存在depart_id的。
在这里插入图片描述
另外:序列化器基类中,重写了create方法,比如,在新建一个 project时,该数据的depart_id,字段就是当前用户的数据。
在这里插入图片描述

过滤类的写法如下:
主要解析:

  1. 用户是超级管理员, 就 所有数据不过滤
  2. 用户(部门id,和角色是必有的,但也要考虑无),根据用户角色们,如果其中有个角色的 data_range为3, 或者有个角色为 管理员admin, 就是 所有数据 不过滤
  3. 根据用户角色们,如果角色们的data_range没有3, 有 data_range为0,那么就 会查业务数据的creator为当前用户的数据,且depart_id 为自己部门的数据, 业务数据没有depart_id也能查
  4. 根据用户角色们,如果角色们的data_range没有3和0,那就是124, 就会查业务数据depart_id满足的数据
    以下代码现有逻辑:当存在多个角色,一个角色为0 仅本人数据, 一个角色为1本部门数据时, 会优先满足0 的情况

此代码仅做参考

  • 因为还没解决仅本人数据,和 本部门数据同时存在的矛盾。、
  • 业务数据是哪个部门的人创建,就属于哪个部门的数据(创建业务数据时,depart_id的存值(上面那张图)),如果业务中,要求各个部门只看自己部门的数据,才有存在的价值
"""
@Remark: 自定义过滤器
"""
from rest_framework.filters import BaseFilterBackend
from system.models import Depart
import django_filters
from system.models import User

class DataLevelPermissionsFilter(BaseFilterBackend):
    """
    功能:《数据级权限过滤器》,步骤如下:
    第一步. 当前用户为超级管理员,则返回所有数据
    第二步. 获取用户所属部门的id,没有部门则返回空数据
    第三步. 判断用户是否分配角色,没有则返回空数据
    第四步. 获取用户所有角色的数据权限列表(会有多个角色,进行去重)------> 得到 data_permissions_list,
        如:[0, 1, 2,4 ],其中0表示本人数据,1表示本部门数据,2表示本部门及以下数据,  (3 添加不进来的)

    第五步. 仅访问本人数据(考虑到部门可能会变更,所以再添加当前部门条件)------如果有0,仅本人数据,就不管有 有角色为124 的数据权限(因为已经提前返回了)
    第六步. 判断业务模型是否有部门ID字段(depart_id),没有返回所有数据

    第七步. 根据各个角色的数据权限部门并集,返回对应部门的数据
    """

    def filter_queryset(self, request, queryset, view):
        # 1.超级管理员,返回所有数据
        if request.user.is_superuser == 0:
            # 2. 获取用户所属部门id,没有部门,则返回None(无数据)
            depart_id = getattr(request.user, 'depart_id', None)
            if not depart_id:
                return queryset.none()

            # 3. 判断用户是否分配角色,没有则仅返回【空】
            if not hasattr(request.user, 'role'):
                return queryset.none()
                # return queryset.filter(depart_id=depart_id) 本部门的数据

            # 4. 获取用户的所有角色列表,得到【数据】范围列表(如:本人数据、本部门数据、本部门及以下数据等)
            role_list = request.user.role.filter(status=1).values('admin', 'data_range')
            data_permissions_list = []
            for ele in role_list:
                # 4.1 全部数据(3)或者角色为管理员,则返回全部数据
                if 3 == ele.get('data_range') or ele.get('admin') == True:
                    return queryset
                data_permissions_list.append(ele.get('data_range'))

            # 4.2 得到所有角色无重复的【数据权限】列表
            data_permissions_list = list(set(data_permissions_list))        # 可能是 [0, 1, 2,4]

            # 5. 仅访问本人数据(考虑到部门可能会变更,所以再添加当前部门条件)
            if 0 in data_permissions_list:
                if not getattr(queryset.model, 'depart_id', None):
                    return queryset.filter(creator=request.user)
                else:
                    # 换部门了,就不能访问原来部门的数据
                    return queryset.filter(creator=request.user, depart_id=depart_id)       # (创建人是当前用户,查询的数据是当前部门的数据)

            # 6. 判断业务模型是否有部门ID字段(depart_id),没有返回所有数据
            if not getattr(queryset.model, 'depart_id', None):
                return queryset

            # 7. 处理:1=本部门数据、2=本部门及以下数据、4=自定数据权限 的情况
            depart_list = []
            for data_range in data_permissions_list:
                if data_range == 4:  # 6.1 自定义数据权限(读取role里面的depart部门)
                    depart_list.extend(request.user.role.filter(status=1).values_list('depart__id', flat=True))
                elif data_range == 2:  # 6.2 本部门及以下数据权限
                    depart_list.extend(get_depart(depart_id, ))
                elif data_range == 1:  # 6.3 本部门数据权限
                    depart_list.append(depart_id)
            return queryset.filter(depart_id__in=list(set(depart_list)))
        else:
            return queryset


# 没用到
class UsersManageFilter(django_filters.rest_framework.FilterSet):
    """
    用户管理 简单过滤器
    URL格式:http://127.0.0.1:8100/?start_time=2022-10-02 08:00:00&end_time=2023-12-31 23:59:59
    """
    # 1.创建时间从
    start_time = django_filters.DateTimeFilter(field_name='create_datetime', lookup_expr='gte')
    # 2.创建时间到
    end_time = django_filters.DateTimeFilter(field_name='create_datetime', lookup_expr='lte')
    # 3.用户名-模糊搜索
    username = django_filters.CharFilter(field_name='username', lookup_expr='icontains')
    # 4.手机号码-模糊搜索
    mobile = django_filters.CharFilter(field_name='mobile', lookup_expr='icontains')

    class Meta:
        model = User
        fields = []


def get_depart(depart_id, depart_all_list=None, depart_list=None):
    """
    递归获取部门的所有下级部门
    :param depart_id: 需要获取的部门id
    :param depart_all_list: 所有部门列表
    :param depart_list: 递归部门list
    :return:
    """
    if not depart_all_list:
        depart_all_list = Depart.objects.all().values('id', 'parent')
    if depart_list is None:
        depart_list = [depart_id]
    for ele in depart_all_list:
        if ele.get('parent') == depart_id:
            depart_list.append(ele.get('id'))
            get_depart(ele.get('id'), depart_all_list, depart_list)
    return list(set(depart_list))

使用自定义类

方式1 笨办法:在新的视图集中,重写filter_backends, 把自定义的类装到列表里即可
在这里插入图片描述
方式2 : 假如已经定义了视图集基类
并且在继承视图集基类后,不想重写filter_backends
在这里插入图片描述
就可以这样:
在基类写一个other_backends, 并重写filter_queryset
在这里插入图片描述

    # 6.重写filter_queryset方法-后端过滤器并集(搜索过滤和并数据过滤)
    def filter_queryset(self, queryset):
        # 取filter_backends 和 other_filter_backends 的无重复并集,data_filter_backends有可能是None
        for backend in set(set(self.filter_backends) | set(self.other_backends or [])):
            queryset = backend().filter_queryset(self.request, queryset, self)
        return queryset

在具体的视图集中,这样写就实现了
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1462886.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

HarmonyOS—添加/删除Module

Module是应用/服务的基本功能单元,包含了源代码、资源文件、第三方库及应用/服务配置文件,每一个Module都可以独立进行编译和运行。一个HarmonyOS应用/服务通常会包含一个或多个Module,因此,可以在工程中创建多个Module&#xff0…

C++模板从入门到入土

1. 泛型编程 如果我们需要实现一个不同类型的交换函数,如果是学的C语言,你要交换哪些类型,不同的类型就需要重新写一个来实现,所以这是很麻烦的,虽然可以cv一下,有了模板就可以减轻负担。 下面写一个适…

Windows下搭建EFK实例

资源下载 elasticSearch :下载最新版本的就行 kibana filebeat:注意选择压缩包下载 更新elasticsearch.yml,默认端口9200: # Elasticsearch Configuration # # NOTE: Elasticsearch comes with reasonable defaults for most …

CSB ---> (XXE)XML基础

本来今天想更一下CSbeacon上线多层的内网机器的,但是刚好今天是年后的第一节课,讲的是XXE的基础,那就来先盘一下基础!! 1.XXE XXE全称是XML External Entity即xml外部实体注入攻击!其后果会导致用户…

UE C++ 设置碰撞前 后事件 碰撞中事件

一.在Actor中声明碰撞BOX组件 UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category "MySceneComponent")class UBoxComponent* MyBox; 在Actor以这样的形式实现代理绑定,在BeginPlay()里。 MyBox->OnComponentBeginOverlap.AddDynamic(); 转到…

《游戏引擎架构》--学习3

内存管理 优化动态内存分配 维持最低限度的堆分配,并且永不在紧凑循环中使用堆分配 容器 迭代器 Unicode

(C++) 详解内存地址空间

详解内存空间 0. 概述 一个C/C 程序,编译之后,形成的程序,在执行期间,内存中不仅存在一块区域用于存放代码,还有一些其他的区域用于使用,本节会详解C/C内部所使用的内存地址空间,关于各内存的…

Java下访问SQLServer 2008(低于2016)数据连接问题

环境:ubuntu20.04,tomcat,java 通过jdbc:sqlserver连接远程的SQLServer 2008R2数据库,2016版本之前仅支持TLS10,因此在连接时会出现如下错误, The driver could not establish a secure connection to SQ…

(二十二)Flask之上下文管理第三篇【收尾—讲一讲g】

目录: 每篇前言:g到底是什么?生命周期在请求周期内保持数据需要注意的是: 拓展—面向对象的私有字段深入讲解一下那句: 每篇前言: 🏆🏆作者介绍:【孤寒者】—CSDN全栈领域…

Java项目:21 基于SSM实现的图书借阅管理系统

作者主页:舒克日记 简介:Java领域优质创作者、Java项目、学习资料、技术互助 文中获取源码 项目介绍 基于SSM实现的图书借阅管理系统设计了两个角色,分别是管理员、用户,在数据表user中以ident字段区分,为1表示管理员…

Vue中如何使用dayjs

Day.js中文网Day.js是一个极简的JavaScript库,可以为现代浏览器解析、验证、操作和显示日期和时间。https://dayjs.fenxianglu.cn/ 单位不区别大小写,支持复数和缩写形式 单位缩写描述 date D日期 [1,31]dayd星期 [0,6](星期日0&#xff0c…

【开源】JAVA+Vue.js实现超市账单管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块三、系统设计3.1 总体设计3.2 前端设计3.3 后端设计在这里插入图片描述 四、系统展示五、核心代码5.1 查询供应商5.2 查询商品5.3 新增超市账单5.4 编辑超市账单5.5 查询超市账单 六、免责说明 一、摘要 1.1 项目介绍 基于…

尾矿库排洪系统结构仿真软件WKStruc(可试用)

1、背景介绍 尾矿库作为重大危险源之一,在国际灾害事故排名中位列第18位,根据中国钼业2019年8月刊《中国尾矿库溃坝与泄漏事故统计及成因分析》的统计,在46起尾矿库泄漏事故中,由于排洪设施导致的尾矿泄漏事故占比高达1/3&#x…

【SpringCloud】使用 Spring Cloud Alibaba 之 Sentinel 实现微服务的限流、降级、熔断

目录 一、Sentinel 介绍1.1 什么是 Sentinel1.2 Sentinel 特性1.3 限流、降级与熔断的区别 二、实战演示2.1 下载启动 Sentinel 控制台2.2 后端微服务接入 Sentinel 控制台2.2.1 引入 Sentinel 依赖2.2.2 添加 Sentinel 连接配置 2.3 使用 Sentinel 进行流控(含限流…

【LeetCode: 105. 从前序与中序遍历序列构造二叉树 + DFS】

🚀 算法题 🚀 🌲 算法刷题专栏 | 面试必备算法 | 面试高频算法 🍀 🌲 越难的东西,越要努力坚持,因为它具有很高的价值,算法就是这样✨ 🌲 作者简介:硕风和炜,…

【精选】Java面向对象进阶——内部类

🍬 博主介绍👨‍🎓 博主介绍:大家好,我是 hacker-routing ,很高兴认识大家~ ✨主攻领域:【渗透领域】【应急响应】 【Java】 【VulnHub靶场复现】【面试分析】 🎉点赞➕评论➕收藏 …

设计师常常从哪些网站获取灵感?

1、Pinterest Pinterest是一个基于图片共享的社交网站。用户可以在平台上浏览、收集和分享各种想法、设计灵感和项目。Pinterest用户可以在其网站或应用程序上创建虚拟画板(boards),根据主题或兴趣收集和整理你最喜欢的图片(包括…

RxJS 核心原理-操作符(源码实现)

本文将深入探讨RXJS操作符的核心原理,并介绍一些常见的操作符、应用场景以及相应的代码示例。通过理解RXJS的响应式编程思想,您将能够更好地应用它来处理异步数据流和事件流。 建议读者在阅读本文之前,先参考我的另一篇文章《深入浅出 RxJS …

IDM(Internet Download Manager)2024免激活绿色版下载

IDM(Internet Download Manager)在安全保护方面提供了多种功能和策略,以确保用户的下载体验和数据安全。以下是一些IDM的安全保护功能和策略: IDM绿色下载如下: https://wm.makeding.com/iclk/?zoneid34275 病毒扫描功能&#…

Spring之AOP源码解析(中)

前言 在上一篇文章中,我们讲解了Spring中那些注解可能会产生AOP动态代理,我们通过源码发现,完成AOP相关操作都和ProxyFactory这个类有密切关系,这一篇我们将围绕这个类继续解析 演示 作用 ProxyFactory采用策略模式生成动态代理对象,具体生成cglib动态代理还是jdk动态代理,…