使用 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 可以在資料庫端一次完成運算,避免先取值再回寫,效能更佳。


2. 為什麼需要 F 表達式?

2-1. 防止競爭條件

想像以下情境:

product.stock -= 1
product.save()
  • 若兩位使用者同時購買同一商品,第一位會將 stock 減 1 並儲存,第二位則會讀取到已減 1 的值,再減 1,結果 stock 會被減 2,產生錯誤。

使用 F 表達式,資料庫會自行完成遞減,避免競爭問題。

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

2-2. 複雜篩選

F 表達式可用於欄位之間的比較。

# 價格大於折扣價的商品
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. 庫存管理

# 減少庫存 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,建議透過資料庫的執行計畫檢視實際效能。


6. 總結

django.db.models.F 表達式是讓你在資料庫層面完成運算的強大工具。 - 解決競爭條件 - 簡化複雜篩選 - 高效處理大量更新

若想更進一步發揮 Django ORM 的潛力,請多加使用 F 表達式。 image

進一步閱讀 - Django 官方文件: F expressions - 實作範例: GitHub Gist

Happy coding! 🚀