Lookup 和 Transform 是 ORM 的扩展点:一个生成比较,一个生成函数。这篇各写一个,看它们怎么出现在 SQL 里、怎么叠加,再验证函数索引到底什么时候能用上。
score__ne=5 → WHERE "lab_person"."score" <> 5 行数: 19999
score__abs=5 → WHERE ABS("lab_person"."score") = 5 行数: 2
score__abs__gt=498 → WHERE ABS("lab_person"."score") > 498 行数: 19003
score__abs__ne=5 → WHERE ABS("lab_person"."score") <> 5 行数: 19998Lookup 继承 Lookup 并实现 as_sql;Transform 继承 Transform,给出 function 和 output_field。装饰器 @Field.register_lookup 把它们挂到字段类上。
values_list("score__abs")[:3] → [500, 499, 498]
order_by("score__abs").first().score = 0
CharField 上有 abs 吗: ProgrammingError: function abs(character varying) does not exist
LINE 1: ...me", "lab_person"."score" FROM "lab_person" WHERE ABS("lab_p..Transform 可以出现在 filter、values、order_by。想限制只给特定字段用,注册到具体的字段类(如 IntegerField)而不是 Field,并且最好在 lookup 里做类型检查(本文没有做)。
annotate(Lower).filter(l="name777") → Index Scan using person_lower_name_idx on lab_person (cost=0.29..8.31 rows=1 width=53)
filter(name__iexact="name777") → Seq Scan on lab_person (cost=0.00..428.00 rows=100 width=21) SQL: WHERE UPPER("lab_person"."name"::text) = UPPER(name777)
filter(name="Name777") → Seq Scan on lab_person (cost=0.00..378.00 rows=1 width=21)数据库只有在查询里的表达式与索引定义完全一致时才会用索引:Lower(name) 索引对应 LOWER(name),而 Django 的 iexact 在 PostgreSQL 上生成的是 UPPER(name) = UPPER(…)。要让 iexact 用上索引,索引表达式要写成 Upper(本文没有验证这一点)。
IntegerField().get_lookup('ne') = NotEqual CharField().get_lookup(ne) = NotEqual
IntegerField 共有 19 个 lookup,含 ne: True get_transform(abs) = AbsoluteValue注册是进程级别的全局操作,模型导入时执行一次。