노트

Django 모델 매니저와 QuerySet

Django Model Managers and QuerySets

백엔드#django#db · 연결된 개념 7개

쉽게 말하면

Django 매니저(objects)는 테이블에 일을 시키는 창구예요. 조건을 이어 붙이는 동안은 장보기 목록에 적기만 하다가, 결과를 실제로 꺼내 볼 때 한 번에 마트에 다녀오는 식으로 쿼리가 나가요.

비유가 깨지는 곳 목록에서 연관 객체를 하나씩 꺼내면 행마다 마트를 다시 다녀오는 셈이에요(N+1). select_related·prefetch_related로 미리 함께 가져오고, 한 번 평가한 QuerySet은 결과를 캐시해요.

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

연결된 개념

이 노트를 가리키는 문서

뜻이 가까운 노트

  • DRF 필터·검색·정렬

    DRF의 목록 API는 쿼리 파라미터로 결과를 좁히는 세 가지 장치를 붙일 수 있다. 값이 정확히 맞는 것만 고르는 필터, 일부 문자열로 찾는 검색, 순서를 바꾸는 정렬이다. 모두 제네릭 뷰(GenericAPIView 계열)의 filter_backends로 적용된다.

  • JPQL과 @Query

    JPQL(Jakarta Persistence Query Language)은 테이블이 아니라 엔티티와 필드를 대상으로 쓰는 JPA의 객체 지향 쿼리 언어다. 실행할 때 연결된 DB의 SQL로 번역된다. Spring Data에서는 @Query로 리포지터리 메서드에 직접 붙인다.

  • 페이지네이션 (offset·cursor)

    페이지네이션(pagination)은 큰 목록을 한 번에 다 주지 않고 조각으로 나눠 주는 방법이다. 크게 오프셋 방식(offset-based, 몇 번째부터 몇 개)과 커서 방식(cursor-based, 이 항목 다음부터 몇 개)으로 나뉜다.

  • Django의 CSRF 방어

    Django는 CsrfViewMiddleware로 CSRF(Cross-Site Request Forgery)를 막는다. 기본 아이디어는 Double Submit Cookie, 즉 쿠키에 든 CSRF 값과 요청에 함께 보낸 값을 비교하는 것이고, 여기에 토큰 마스킹(token masking)과 Origin·Referer 검사를 더한다.

  • Django 미들웨어와 로그인 흐름

    Django 미들웨어는 모든 요청과 응답을 감싸는 훅 체인(hook chain)이다. settings.MIDDLEWARE에 적힌 순서대로 요청이 들어가고, 응답은 그 역순으로 나온다. 세션 로드, CSRF 검사, 사용자 식별 같은 공통 처리가 여기서 일어난다.

보기 옵션