当前位置: 首页 > news >正文

Django一分钟:lookupAPI详解,使用django orm生成高效的WHERE子句

一、Lookup API概述

Lookup API是Django用于构建数据库查询WHERE子句的API。

Lookup API的核心包含两部分:

  • RegisterLookupMixin:为子类提供注册lookup的方法
  • Query Expression API:一个接口,规定了可以被注册为lookup的类需要实现的方法。

在Django中有两个实现了Query Expression API的基类分别是:LookupTransform;前者用于声明查询条件,后者专注于对字段做转换。两者都实现了Query Expression API,都可以被注册为lookup,可以单独使用,也可以互相配合。

lookup是一个比较抽象的名词,它描述的是搜索和过滤条件,而不是查找这个动作本身。对字段注册lookup,我们就能构造查询表达语句,Django会将其编译为合适的SQLWHERE子句。

二、Lookup基类

前面提到实现Query Expression API的基类分别是LookupTransform。首先介绍Lookup类,它是我们构造查询语句的主力,我们从自定义一个Lookup的子类出发来了解它的工作原理。

1. 自定义Lookup

尝试实现这样一件事情,将User.objects.filter(username__is='Mario')转化为SQL:

"user"."username" IS 'mario'

当然SQL语句中不存在"IS",这样只是方便理解。实现这个功能需要两个步骤:

  1. 声明一个Lookup的子类,并重写as_sql()方法:
from django.db.models import Lookupclass Is(Lookup):lookup_name = "is"def as_sql(self, compiler, connection):lhs, lhs_params = self.process_lhs(compiler, connection)rhs, rhs_params = self.process_rhs(compiler, connection)params = lhs_params + rhs_paramsreturn "%s IS %s" % (lhs, rhs), params
  1. 注册新声明的lookup类Is
    需要lookup在某个字段上生效,就需要调用这个字段上的register_lookup方法进行注册。Filed继承了RegisterLookupMixin类,因此可以使用其提供的register_lookup方法注册lookup。为Field注册lookup,其所有子类就都可以使用此lookup:
from django.db.models import FieldField.register_lookup(Is)

Filed提供了装饰器方法注册lookup:

from django.db.models import Field@Field.register_lookup
class Is(Lookup): ...

使用新定义的Is查找类的方法有两种:

  1. 构造<lhs>__<lookup_name>=<rhs>形式的查询表达语句:
from django.contrib.auth.models import User
q = User.objects.filter(username__is="mairo")
q.query
print(q.query)

编译的SQL语句:

SELECT ...略 FROM "auth_user" WHERE "auth_user"."username" IS mairo
  1. 使用创建实例的形式
q = User.objects.filter(username__is="mairo")from django.db.models import F
q2 = User.objects.filter(Is(F('username'), 'mario'))

编译的SQL语句与上面的方法完全一致,两种方法是等效的。

2. Django内置的lookup

Django内置了很多lookup,在django.db.models.lookups目录下你可以找到它们:

...
@Field.register_lookup
class GreaterThan(FieldGetDbPrepValueMixin, BuiltinLookup):lookup_name = "gt"@Field.register_lookup
class GreaterThanOrEqual(FieldGetDbPrepValueMixin, BuiltinLookup):lookup_name = "gte"
...

介绍两种常用的内置lookup:

  • exact
    默认情况下当你没有提供查找类型的时候默认使用exact:
Entry.objects.get(id=14)
Entry.objects.get(id__exact=14)
# 上面两个完全一致,准确来说django将id=14默认视为id__exact=14

等效SQL语句:

SELECT ... WHERE id = 14;
  • in
Entry.objects.filter(id__in=[1, 3, 4])

等效SQL语句:

SELECT ... WHERE id IN (1, 3, 4);

三、Transform基类

Lookup类相同Transform类及其子类也可以被注册为lookup,但TransformLookup不同,Transform 更加关注的是如何改变字段值,而不是直接进行值的比较。通过使用 Transform,你可以对数据库中的数据执行诸如转换为小写、截取字符串的一部分、日期时间的格式化等操作。通常会和Lookup配合使用,它主要做预处理工作。它是数据库级别的转换,具有很好的性能。

1. 自定义Transform

from django.db.models import Transform
from django.db.models import IntegerFieldclass AbsoluteValue(Transform):lookup_name = "abs"function = "ABS"IntegerField.register_lookup(AbsoluteValue)

Transform在源码中继承了两个类class Transform(RegisterLookupMixin, Func),可以说它是一个特殊的Func,在声明时就规定好了用于转换的function,因此只接收一个参数。

  • 使用示例1:
Experiment.objects.filter(change__abs=27)

生成的SQL:

SELECT ... WHERE ABS("experiments"."change") = 27

注意这里django实际上将change__abs=27 视作 change__abs__exact=27

  • 使用示例2:
Experiment.objects.filter(change__abs__lt=27)

生成的SQL:

SELECT ... WHERE ABS("experiments"."change") < 27

2. 内置的Transform

  • year
Blog.objects.filter(time__year__gt=2023)

生成的SQL

SELECT ... FROM "snippets_blog" WHERE "snippets_blog"."time" > 2023-12-31 23:59:59.999999+00:00

Django内置了很多中方便的transform,这里不做赘述。

四、lookup表达语句的构成

一个lookup表达语句可以由三部分构成,组合使用可以构造强大高效的查询语句。

  • field:如Book.objects.filter(author__best_friends__first_name...)
  • Transform:如__lower__first3chars__reversed
  • lookup:如__icontains默认__exact

五、总结

  • lookup API是Django中一个很重要的功能,它的主要工作就是构造查询的WHERE子句。
  • Django已经内置了很多使用的lookups帮助我们快速构造查询语句。
  • Django允许我们自定义lookups实现复杂的查询需求。
http://www.lryc.cn/news/444027.html

相关文章:

  • 信息安全工程师(8)网络新安全目标与功能
  • 返利机器人在电商返利系统中的负载均衡实现
  • MATLAB中typecast函数用法
  • 植物大战僵尸【源代码分享+核心思路讲解】
  • 变压器设备漏油数据集 voc txt
  • 算法练习题25——leetcode3279统计重新排列后包含另一个字符串的子字符串的数目(滑动窗口 双指针 哈希)
  • JavaEE: 深入探索TCP网络编程的奇妙世界(二)
  • GPT1-GPT3论文理解
  • C/C++内存管理 ——
  • 深度学习02-pytorch-04-张量的运算函数
  • OpenHarmony(鸿蒙南向开发)——小型系统内核(LiteOS-A)【文件系统】上
  • NISP 一级 | 8.4 《网络安全法》
  • 实现人体模型可点击
  • C++ | Leetcode C++题解之第429题N叉树的层序遍历
  • Pandas简介
  • Python | Leetcode Python题解之第430题扁平化多级双向链表
  • 机器人机构、制造
  • 《拿下奇怪的前端报错》:nvm不可用报错`GLIBC_2.27‘‘GLIBCXX_3.4.20‘not Found?+ 使用docker构建多个前端项目实践
  • 5.《DevOps》系列K8S部署CICD流水线之K8S通过Yaml部署GitLab
  • [SAP ABAP] 创建数据库视图和维护视图
  • 【最快最简单的排序 —— 桶排序算法】
  • AI时代,服务器厂商能否打破薄利的命运?
  • 2024年9月python二级易错题和难题大全(附详细解析)(二)
  • 4.结构型设计模式 - 第1回:引言与适配器模式 (Adapter Pattern) ——设计模式入门系列
  • 解决mybatis plus 中 FastjsonTypeHandler无法正确反序列化List类型的问题
  • MacOS安装homebrew,jEnv,多版本JDK
  • 【HTTP】认识 URL 和 URL encode
  • 【AI学习笔记】初学机器学习西瓜书概要记录(二)常用的机器学习方法篇
  • [SDX35+WCN6856]SDX35 + WCN6856 默认增加打包wifi配置hostapd_24g.conf和hostapd_5g.conf操作方法
  • 【iOS】OC高级编程 iOS多线程与内存管理阅读笔记——自动引用计数