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