Django 모델 클래스는 DB 테이블에 대응하고, 모델의 매니저(Manager)가 그 테이블에 쿼리를 보내는 입구다. 기본 매니저 이름이 objects이고, 매니저가 돌려주는 QuerySet에 조건을 이어 붙여 쿼리를 만든다.
class Movie(models.Model):
title = models.CharField(max_length=100)
active = models.BooleanField(default=True)
created = models.DateTimeField(auto_now_add=True)
Movie.objects.all()
Movie.objects.filter(active=True).exclude(title="").order_by("-created")[:10]
Movie.objects.get(pk=1) # 0개나 여러 개면 예외
Movie.objects.create(title="Dune")QuerySet의 성질
- 지연 평가(lazy evaluation):
filter()를 이어 붙이는 동안에는 쿼리가 나가지 않는다. 순회·len()·list()·bool()할 때 실행된다. 슬라이싱([:10])은 보통LIMIT이 붙은 새 QuerySet을 돌려줄 뿐 바로 실행하지 않는다 - 한 번 평가된 QuerySet은 결과를 캐시한다. 같은 변수를 다시 순회하면 쿼리가 또 나가지 않는다
- 관계 필드는
__로 따라간다:Review.objects.filter(watchlist__platform__name="Netflix") - 연관 객체를 목록에서 접근하면 행마다 쿼리가 나간다.
select_related(정방향 FK, 조인)와prefetch_related(역방향·M2M, 별도 쿼리 후 합침)로 미리 가져온다(N+1 문제)
커스텀 매니저(custom manager)
반복되는 조건을 매니저나 QuerySet 메서드로 이름 붙인다.
class MovieQuerySet(models.QuerySet):
def active(self):
return self.filter(active=True)
class Movie(models.Model):
...
objects = MovieQuerySet.as_manager()
Movie.objects.active().order_by("title")- 업무 의미가 담긴 이름("활성 영화")이 쿼리 조건을 감춰 읽기 쉬워진다(함수 추출하기)
- 기본 매니저를 필터링된 매니저로 바꾸면 관리자 화면·관계 조회에도 영향이 가므로 조심한다
시각 필드
auto_now_add=True는 생성 시각, auto_now=True는 저장할 때마다 수정 시각을 채운다. 스프링의 JPA Auditing과 같은 역할이다. 단 QuerySet.update()는 save()를 거치지 않아 auto_now가 갱신되지 않는다.
스프링 쪽 대응은 Spring Data 리포지터리와 쿼리 메서드, DB와 모델이 어긋나는 문제는 스키마 드리프트를 본다.
출처: Django 문서: QuerySets are lazy · Caching and QuerySets · select_related · prefetch_related · Custom managers