Django - Custom Manager, QuerySet
Web/Django

Django - Custom Manager, QuerySet

뉴비뉴 2019. 11. 20.

https://wikidocs.net/6668

 

위키독스

온라인 책을 제작 공유하는 플랫폼 서비스

wikidocs.net

models.ManagerDjango 모델에 데이터베이스 쿼리 작업이 제공되는 인터페이스 입니다.

Manager는 Django 애플리케이션의 모든 모델에 대해 하나 이상 존재합니다.

 

모델 매니저의 Default 값은 objects로 되어 있습니다.

class TestManager(models.Manager):
	def is_display(self):
    	return self.get_queryset().filter(is_display=True)
        

class Test(models.Model):
	objects = TestManager()

 

커스텀 모델 매니저를 구현하는 방법에는 크게 두 가지가 있습니다.

 

- 모델 매니저를 매번 추가해서 특정 모델 매니저 인스턴스를 이용하는 방식

class PublishedManager(models.Manager):
    def get_queryset(self):
        return super(PublishedManager, self).get_queryset().filter(status='published')

- Default 모델 매니저를 변경 후 메소드 체인으로 호출하는 방식

class Post(models.Model):
    objects = models.Manager()
    published = PublishedManager()

    STATUS_CHOICES = (
        ('draft', 'Draft'),
        ('published', 'Published'),
    )

    publish = models.DateTimeField(default=timezone.now)

    ... 생략 ...

- 커스텀 모델 매니저의 메소드 호출 예시

>>> from blog.models import Post
>>> Post.objects.count()
2
>>> Post.published.count()
1

https://brownbears.tistory.com/434

 

[Django] Custom Manager, Custom QuerySet

Django 모델에서 Manager는 데이터베이스와 상호 작용하는 인터페이스입니다. 기본적으로 Manager는 Model.objects 속성을 통해 사용할 수 있습니다. Django 모델마다 기본적으로 사용되는 기본 관리자는 django.d..

brownbears.tistory.com

Django 모델에서 Manager는 데이터베이스와 상호 작용하는 인터페이스이다. 기본적으로 Manager는 Model.objects 속성을 통해

사용할 수 있습니다.

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) # size보다 작은 것을 filter해라


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


    def __str__(self):
        return self.name

dc = DocumentManager()나 objects = DocumentManager()를 사용하지 않으면 class DocumentManager(models.Manager):

를 사용할 수 없게된다. 간단하게 정리하면 Manager를 만들면 Model에서 그 매니저를 받을 변수?속성을 지정해줘야한다.

지정을 해줘야 Document.dc.pdfs(), Document.dc.pdfs().filter(name='test') 와 같은 것을 사용할 수 있다.

 

하지만 Manager에서 선언한 2개의 메소드(pdfs, smaller_than) 중 아래와 같이 다른 메소드를 호출하고자하면 오류가 발생한다.

Document.dc.pdfs().smaller_than(1000)

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

이 문제를 해결하는 것이 바로 QuerySet을 선언하는 것이다.

from django.db import models


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

    def smaller_than(self, size):
        return self.filter(size__lt=size) # size보다 작은 것을 filter해라


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)
    dc = DocumentManager()

    def __str__(self):
        return self.name

위와 같이 설정하게 되면 아래와 같이 사용할 수 있다.

Document.dc.pdfs().smaller_then(1000)

Manager와 QuerySet을 정의하게 되면 장점이 어떻게 될까? 위의 예시를 들어 생각을 해보면 문서를 업로드하는데

해당 문서의 사이즈가 1000보다 작으면서 pdf 파일이여야 한다는 조건을 걸어야 된다고하면 위의 예시처럼 사용하면 된다.

models.py 안에 조회 쿼리를 넣을 수 있지만 코드가 커진다면 Manager와 QuerySet을 managers.py 라는 모듈에 유지하는 것을 추천한다.

Custom Manager와 Custom QuerySet을 사용하는 이점은 공통적으로 사용되는 쿼리를 공통 함수로 정의할 수 있고 실제 동작을 숨길 수 있습니다. 그리고 또 다른 장점은 당연히 재사용성이 뛰어나다는 점 일 것 같다.

 

응용

from django.db import models


class PostQuerySet(models.QuerySet):

    def is_display(self):
        return self.filter(is_display=True)

    def is_hot(self):
        return self.filter(is_hot=True)

    def random(self):
        return self.order_by('?')


class PostManager(models.Manager):

    def get_queryset(self):
        return PostQuerySet(self.model, using=self._db)

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

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

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

오늘 알아본 것을 토대로 위의 코드를 분석해보자면

A가 글을 작성하여 DB에 저장되어있다.

나는 사용자가 작성한 글에 is_hot으로 인기글로 올릴 수 있고, 정렬을 랜덤으로 나오게 할수도 있고,

화면에 안나오게 나오게 설정할 수 있다. 

 

나는 A의 글을 인기글로 올리고싶고, 화면에 출력되게 하고 싶다면 아래와 같이 코드를 작성하면 될 것이다.

def queryset(self, **kwargs):
	return Post.obects.is_display().is_hot()

 

댓글

💲 추천 글