ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Django] Custom Manager, Custom QuerySet
    언어/파이썬 & 장고 2019. 2. 2. 18:12

    Django 모델에서 Manager는 데이터베이스와 상호 작용하는 인터페이스입니다. 기본적으로 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을 사용하는 이점은 공통적으로 사용되는 쿼리를 공통 함수로 정의할 수 있고 실제 동작을 숨길 수 있습니다.

    댓글