Djangoを使うとき、データベースに直接クエリを書かずに複雑な演算を行いたい瞬間が多々あります。
そのときに便利なのが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()
  • 同じ商品を同時に購入した2人がいると、最初の人がstockを1減らして保存した後、2人目が同じ値を読み取り再び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. Fオブジェクトと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. Fオブジェクトと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はFオブジェクトを使った*演算をサポートしないことがあるので、使用前にテストが必要です。


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. 大量価格上げ

# 特定カテゴリのすべての商品価格を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

Tip: Fオブジェクトを使ったクエリはSQLに直変換されるので、プロファイリングで実際の実行計画を確認すると良いでしょう。


6. まとめ

django.db.models.Fオブジェクトはデータベースレベルで演算を行う強力なツールです。
- 同時性問題を解決し、
- 複雑なフィルタリングを簡素化し、
- 大量更新を効率的に処理できます。

Django ORMをさらに活用したいなら、Fオブジェクトを積極的に使ってみてください。
image

追加資料
- Django公式ドキュメント: F expressions
- 実践例コード: GitHub Gist

Happy coding! 🚀