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 평가됨):
for
문에서 순회할 때list(qs)
를 호출할 때bool(qs)
(if qs:
같은 조건문에서) 호출할 때.count()
,.exists()
,.first()
,.last()
등의 메서드를 호출할 때
📌 결론
✅ .only()
→ 모델 객체 유지 + 특정 필드만 미리 로드
🔹 ORM 기능 (.save()
, .delete()
) 사용 가능
🔹 하지만 미리 로드되지 않은 필드 접근 시 추가 쿼리 발생
✅ .values()
→ 딕셔너리 리스트 반환 + 추가 쿼리 없음
🔹 모델 객체가 아니므로 .save()
, .delete()
사용 불가
🔹 QuerySet 자체는 유지되므로 .filter()
, .annotate()
사용 가능
🔹 ForeignKey 필드는 기본적으로 ID 값만 반환 → author__name
처럼 지정해야 함
💡 즉, 모델 객체를 유지해야 하면 .only()
를, 단순 데이터 조회 시 .values()
를 선택하자! 🚀
🎯 Django ORM을 사용할 때 성능 최적화가 중요합니다.
어떤 기능을 사용할지 고민될 때, 이 글을 참고하여 애플리케이션에 맞는 최적의 방식을 선택하길 바랍니다.
여러분의 프로젝트가 더욱 효율적이고 빠르게 동작하길 응원합니다! 🔥
Add a New Comment