본문 바로가기
  • 기억보단 기록을..
Python/Django

Custom Manager, QuerySet

by reine94._.dev 2022. 4. 5.

Manager

Django 모델에서 데이터베이스와 상호 작용하는 인터페이스

기본적으로 Manager는 Model.objects 속성을 통해 사용할 수 있다.
Django 모델마다 기본적으로 사용되는 기본 관리자는 django.db.models.Manager 이다.

from django.db import models

class DocumentManager(models.Manager):
    def pdfs(self):
        return self.filter(file_type='pdf')

    def smaller_than(self, size):
        return self.filter(size__lt=size)

class Document(models.Model):
    name = models.CharField(max_length=30)
    size = models.PositiveIntegerField(default=0)
    file_type = models.CharField(max_length=10, blank=True)

    objects = DocumentManager()

위 예시에서 filter_type = "pdf" 를 선언하는 방법은 아래와 같습니다.
또한 호출 후, filter()order_by() 같이 추가적으로 연결하여 사용할 수 있습니다.

Document.objects.pdfs()

Document.objects.pdfs().filter(name='test')
Document.objects.pdfs().order_by('name')

하지만 Manager에 선언한 다른 메소드를 호출하고자 하면 오류가 발생합니다.

Document.objects.pdfs().smaller_than(1000)

# AttributeError: 'QuerySet' object has no attribute 'smaller_than'

이 문제를 해결하는 것은 Custom QuerySet을 선언하는 것입니다.

class DocumentQuerySet(models.QuerySet):
    def pdfs(self):
        return self.filter(file_type='pdf')

    def smaller_than(self, size):
        return self.filter(size__lt=size)

class DocumentManager(models.Manager):
    def get_queryset(self):
        return DocumentQuerySet(self.model, using=self._db)  # 중요

    def pdfs(self):
        return self.get_queryset().pdfs()

    def smaller_than(self, size):
        return self.get_queryset().smaller_than(size)

class Document(models.Model):
    name = models.CharField(max_length=30)
    size = models.PositiveIntegerField(default=0)
    file_type = models.CharField(max_length=10, blank=True)

    objects = DocumentManager()


Document.objects.pdfs().smaller_than(1000).exclude(name='Article').order_by('name')

만약 Custom QuerySet만 정의하고자 하면 아래와 같이 선언하면 됩니다.

class DocumentQuerySet(models.QuerySet):
    def pdfs(self):
        return self.filter(file_type='pdf')

    def smaller_than(self, size):
        return self.filter(size__lt=size)

class Document(models.Model):
    name = models.CharField(max_length=30)
    size = models.PositiveIntegerField(default=0)
    file_type = models.CharField(max_length=10, blank=True)

    objects = DocumentQuerySet.as_manager()

pdfs()만 호출할 수 있는 것은 물론, smaller_than() 또한 연결하여 호출할 수 있습니다.

Document.objects.pdfs().smaller_than(1000)

models.py 안에 조회 쿼리를 넣을 수 있지만 코드가 커진다면 Manager와 QuerySet을 managers.py라는 다른 모듈에 유지하는 것을 추천합니다. Custom Manager와 Custom QuerySet을 사용하는 이점은 공통적으로 사용되는 쿼리를 공통 함수로 정의할 수 있고 실제 동작을 숨길 수 있습니다.

댓글