Django ORMを使用するときにパフォーマンスを最適化するために特定のフィールドのみを取得する方法として、.values().only()メソッドがあります。
以前の投稿でDjango QuerySetの .values() メソッドとは?について詳しく取り上げましたので、まだご覧になっていない方は一度チェックしてみることをお勧めします。😊

以前の記事を確認するには、クリック! 

Django QuerySetの .values() メソッドとは?

.values()に関する記事を書いていたら、これと似ているけれども異なる機能を持つ.only()メソッドも一緒に説明することがDjango ORMをより深く理解するのに役立つだろうと思い、今回の記事を準備しました。🚀

1️⃣ .only().values() の違い

  .only() .values()
返却形式 モデルインスタンス (Modelオブジェクト) 辞書リスト (dict list)
使用目的 モデルオブジェクトを保持 + 一部フィールドのみロード 特定のフィールドのみを取得し辞書リスト形式のクエリセットを返却
ORM機能保持の有無 ✅ 保持される (.save(), .delete() 使用可能) ❗ モデルメソッドは使用不可 (.save(), .delete() ❌)
追加のLazy Loading ❗ プリロードされていないフィールドにアクセスすると追加のクエリが発生 ✅ 追加のクエリは発生しない

2️⃣ .only()の使用法と特徴

.only()モデルオブジェクトを保持しながら特定のフィールドのみをプリロードする方法です。しかし、事前に指定していないフィールドにアクセスするとLazy Loadingが発生し、追加のクエリが実行されることがあります。

from myapp.models import Post

# titleとauthorフィールドのみをロードするQuerySet
qs = Post.objects.only("title", "author")

# 出力確認 (QuerySet自体は遅延評価される)
print(qs)  # SQLは実行されない

# フィールドにアクセス
for post in qs:  # ✅ この時点でSQLが実行される
    print(post.title)  # ✅ プリロードされているフィールド (追加のクエリはなし)
    print(post.body)   # 🚨 プリロードされていないフィールド → 追加のクエリが発生

🔹 注意点:
.only()指定したフィールドのみ即時にロードし、他のフィールドは必要に応じて追加のクエリを実行します。
したがって、不要なクエリを防ぐためにはすべての必要なフィールドを.only()で指定することが推奨されます。

3️⃣ .values()の使用法と特徴

.values()は特定のフィールドのみを取得し、辞書リスト形式で返します。モデルオブジェクトではなく、辞書を返すためORM機能は使用できません。
しかし、QuerySet自体は保持されるため、.filter().annotate()のようなメソッドは引き続き使用可能です。

# titleとauthorフィールドのみを取得し、結果を辞書形式で返却
qs = Post.objects.values("title", "author")

# QuerySet自体は遅延評価される (SQL実行しない)
print(qs)

# 辞書データを活用 (この時点でSQLが実行される)
for post in qs:
    print(post["title"])  # ✅ 追加のクエリは発生しない

🔹 注意点:
.values()を使用するとForeignKeyフィールドは基本的にID値のみを返すことに注意が必要です。
したがって、関係モデルの特定のフィールド値が必要であれば"author__name"のように明示的に指定する必要があります。

# ForeignKeyフィールドの特定の値を取得
qs = Post.objects.values("title", "author__name")

for post in qs:
    print(post["author__name"])  # ✅ authorオブジェクトのnameフィールドを取得

上の例のコードはForeignKeyで接続された"author__name"フィールドを指定しているため、結果として意図したauthor.nameが出力されますが、"author"と指定した場合は単にauthorオブジェクトのID番号だけが出力されることになります。 

4️⃣ .only().values() はいつ使用すべきか?

使用目的 .only() 推奨 .values() 推奨
モデルオブジェクトを保持しなければならない場合
ORM機能 (.save(), .delete(), .update()) 使用の必要
パフォーマンス最適化 (不要なフィールドのロードを防ぐ)
追加的なLazy Loadingを防ぐ ❌ (追加クエリの可能性) ✅ (追加クエリなし)

5️⃣ .values() も遅延評価される!

.values()は即時評価されるように見えるかもしれませんが、実際にはDjangoのQuerySetは基本的にすべて遅延評価(Lazy Evaluation)方式です。
すなわち、.values()を呼び出す瞬間にSQLが実行されるわけではなく、実際のデータを使用しようとしたときに評価されます。 🚀

.values() も遅延評価される (SQLは即時実行されない)

qs = Post.objects.values("title", "author")  # ❌ SQLは実行されない
print(qs)  # ❌ QuerySetオブジェクト情報のみを出力 (SQLは実行されない)

for post in qs:  # ✅ この時点でSQLが実行される
    print(post["title"])

✅ いつQuerySetが評価されるのか?

.values()を含むQuerySetは次のような場合に即時実行(SQL評価される):

  1. for文でループするとき
  2. list(qs)を呼び出すとき
  3. bool(qs) (if qs:のような条件文で) を呼び出すとき
  4. .count(), .exists(), .first(), .last() などのメソッドを呼び出すとき

📌 結論

.only()モデルオブジェクトを保持 + 特定のフィールドのみをプリロード
     🔹 ORM機能 (.save(), .delete()) 使用可能
     🔹 ただしプリロードされていないフィールドにアクセスすると追加クエリが発生

.values()辞書リストを返却 + 追加クエリなし
     🔹 モデルオブジェクトではないため.save(), .delete()は使用不可
     🔹 QuerySet自体は保持されるため.filter(), .annotate() 使用可能
     🔹 ForeignKeyフィールドは基本的にID値のみを返す → author__name のように指定する必要がある

💡 つまり、モデルオブジェクトを保持しなければならないときは.only()を、単純なデータ取得時は.values()を選ぼう! 🚀

🎯 Django ORMを使用する際はパフォーマンス最適化が重要です。

どの機能を使用するか迷ったときは、この文章を参考にしてアプリケーションに合った最適な方法を選択してください。
皆さんのプロジェクトがより効率的で迅速に動作することを応援しています! 🔥

Django QuerySet .only() vs .values() flowchart