使用 django.db.models.F 对象的 Django ORM 强大功能

在使用 Django 的过程中,常常会遇到 不想直接写 SQL 查询却需要执行复杂运算 的情况。此时,django.db.models.F 对象就显得非常有用。

本文将介绍 F 对象是什么、何时以及如何使用,并通过若干实战示例展示其强大之处。


1. F 对象是什么?



F 对象是 引用数据库字段值 的表达式。

也就是说,它不是模型实例,而是在 数据库层面 读取字段值,并可用于与其他字段比较或参与运算。

from django.db.models import F

核心要点 - F 对象不是 Python 对象,而会被转换为 SQL 表达式。 - 使用 F 可以让 数据库一次性完成运算,比在 Python 中先取值再计算更高效。


2. 为什么需要 F 对象?

2-1. 防止并发问题

想象一下下面的场景:

product.stock -= 1
product.save()
  • 两个用户同时购买同一商品时,第一位用户会把 stock 减 1 并保存,随后第二位用户读取到同样的值再次减 1。
  • 结果导致 stock 被减 2,出现错误。

使用 F 对象,数据库自身完成递减,从而避免并发冲突。

Product.objects.filter(id=product_id).update(stock=F('stock') - 1)

2-2. 复杂过滤

借助 F 对象,可以 与其他字段比较

# 只查询 price 大于 discount_price 的商品
Product.objects.filter(price__gt=F('discount_price'))

2-3. 批量更新

F 对象可以让 一次查询 更新 多条记录

# 所有商品价格上涨 10%
Product.objects.update(price=F('price') * 1.10)

3. F 对象的使用方法



3-1. 基本用法

from django.db.models import F

# 字段间比较
qs = Book.objects.filter(pages__gt=F('chapters'))

# 字段间运算
qs = Book.objects.update(pages=F('pages') + 10)

3-2. 与 Q 对象结合

使用 Q 对象可以构造 更复杂的条件

from django.db.models import Q

qs = Book.objects.filter(
    Q(pages__gt=F('chapters')) | Q(pages__lt=F('pages') * 2)
)

3-3. 与 annotate 结合

annotate 可以创建 虚拟字段 并加入查询集。

from django.db.models import F, Value, FloatField

qs = Book.objects.annotate(
    ratio=F('pages') / F('chapters')
).filter(ratio__gt=5)

注意F 对象的运算支持程度因数据库而异。例如,SQLite 可能不支持 * 运算,需要先行测试。


4. 实战示例

4-1. 库存管理

# 递减库存,若库存降至 0 则禁用
Product.objects.filter(id=product_id, stock__gt=0).update(
    stock=F('stock') - 1,
    is_active=Case(
        When(stock=1, then=Value(False)),
        default=Value(True),
        output_field=BooleanField()
    )
)

4-2. 折扣率计算

# 只查询折扣率 >= 20% 的商品
Product.objects.annotate(
    discount_rate=F('discount_price') / F('price')
).filter(discount_rate__gte=0.2)

4-3. 批量价格上涨

# 将 electronics 类别下所有商品价格上涨 15%
Product.objects.filter(category='electronics').update(
    price=F('price') * 1.15
)

5. 使用 F 对象的技巧

场景 推荐用法
并发 update() + F
字段比较 filter(field__gt=F('other_field'))
虚拟字段 annotate(new_field=F('field1') + F('field2'))
条件更新 Case + When + F

TipF 对象生成的查询会直接转为 SQL,建议通过 profiling 查看实际执行计划。


6. 结语

django.db.models.F 对象是 在数据库层面 进行运算的强大工具。 - 它能 解决并发问题, - 简化复杂过滤, - 以及 高效批量更新

想进一步提升 Django ORM 的使用体验,建议多尝试 F 对象。

image

更多资料 - Django 官方文档: F expressions - 实战代码示例: GitHub Gist

Happy coding! 🚀