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() vs .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