-
Django ORM Manager, QuerySetDjango 2022. 9. 26. 23:06
Django Manager? QuerySet?
Django의 manager란 DB와 상호작용하는 인터페이스 즉 테이블 단위의 작업이다.
장고의 Model에 objects라는 이름으로 Manager가 구현되어있다.
기본적으로 사용하는 get(), filter(), create(), delete(), update(), exists(), aggregate(), annotate() 등등이 _QuerySet안에 구현되어있다.Custom manager, Custom QuerySet
왜 사용하고 필요한지 간단한 예시를 들어보자
from django.db import models class TeamManager(models.Manager): pass class Team(models.Model): name = models.CharField( verbose_name=_("팀 이름"), help_text=_("팀의 이름입니다."), max_length=30, blank=True, ) objects: TeamManager = TeamManager() class Meta: verbose_name = verbose_name_plural = _("팀") def __str__(self) -> str: return f"{self.name}팀"이런 모델이 있다고 생각해보자
TeamManager에는 models.Manager를 상속받아 평소에 사용하는 Manager들을 사용할 수 있다.
그리고 유저 모델은 이렇게 생겼다고 가정해보자from django.db import models class UserManager(models.Manager): pass class User(models.Model): name = models.CharField( verbose_name=_("유저 이름"), help_text=_("유저의 이름입니다."), max_length=10, ) team = models.ForeignKey( "team.team", verbose_name=_("팀"), help_text=_("소속한 팀 입니다."), related_name="users", on_delete=models.CASCADE, ) power = models.PositiveIntegerField( verbose_name=_("전투력"), help_text=_("유저의 전투력입니다."), default = 100, ) is_active = models.BooleanField( verbose_name=_("활동 상태"), help_text=_("유저의 활동 상태입니다."), default = False, ) objects: UserManager = UserManager() class Meta: verbose_name = verbose_name_plural = _("유저") def __str__(self) -> str: return f"{self.name}"유저 모델부터 살펴보자
만약 현재 활동하고 있는 유저를 찾는다라면User.objects.filter(is_active=True)이런 식으로 찾을 수 있을 것이다.
전투력이 100이 넘는 유저를 찾고 싶다면User.objects.filter(power__gt=100)이런 식으로 찾을 수 있을 것이다.
이 둘을 모두 만족하는 유저들을 찾는다면User.objects.filter(power__gt=100, is_active=True)이런 식으로 찾을 수 있을 것이다
현재까지 아무런 필요성을 못 느끼게 된다.
하지만 활동 중이고 전투력이 100이 넘는 유저를 찾는 것이 플레이하고 있는 유저라는 기준으로 되어 이 쿼리 셋이 아주 자주 사용된다면?
그 부분마다 위의 쿼리 셋이 중복이 되고 유지보수 시 모든 부분을 변경해주어야 하는 문제가 발생한다.
그런 부분을 해결하기 위한 것이 custom manager이다.class UserManager(models.Manager): def is_playuser(self): # 여기서의 self란 User.objects return self.filter(is_active=True, power__gt=100)그렇다면 models.QuerySet의 역할은 무엇일까?
위의 쿼리 셋이 is_active=True인 유저, 전투력이 100 넘는 유저도 따로따로 자주 사용된다고 가정해보자class UserManager(models.Manager): def is_active(self): return self.filter(is_active=True) def exclude_default_power(self): return self.filter(power__gt=100)이런 식으로 따로 두고 사용하게 될 것이고 둘 다 만족하는 경우를 구할 시
User.objects.is_active().exculde_default_power()하나 이런 식으로 사용할 시 에러가 발생한다.
이런 custom manager 끼리의 chain과 manager에서도 중복을 방지하기 위해 custom queryset을 사용한다.
내부적으로 정확히는 self.filter 이런 식으로 구현이 되어있는 것이 아니라 self.get_queryset() 메서드를 이용하여 구현이 되어있다.
get_queryset()이란 타깃으로 할 모델의 쿼리 셋 default는 모델의 모든 데이터이다.class UserQuerySet(models.QuerySet): def is_active(self): return self.filter(is_active=True) def exclude_default_power(self): return self.filter(power__gt=100) class UserManager(models.Manager): def get_queryset(self): return UserQuerySet(self.model, using=self._db) def is_active(self): return self.get_queryset().is_active() def exclude_default_power(self): return self.get_queryset().exclude_default_power()이런 식으로 완성해 중복을 피하고 유지보수 또한 용이해진 manager와 queryset이 완성되었다.
마지막으로 예시를 위해 각 팀에 속한 유저들의 전투력을 모두 합한 값을 구하는 manager, queryset을 구현해보자.from django.db import models from django.db.models import Sum class TeamQuerySet(models.QuerySet): def team_power(self): return self.aggregate(team_power=Sum("users__power"))["team_power"] class TeamManager(models.Manager): def get_queryset(self): return TeamQuerySet(self.model, using=self._db) def team_power(self): return self.get_queryset().team_power() class Team(models.Model): name = models.CharField( verbose_name=_("팀 이름"), help_text=_("팀의 이름입니다."), max_length=30, blank=True, ) objects: TeamManager = TeamManager() class Meta: verbose_name = verbose_name_plural = _("팀") def __str__(self) -> str: return f"{self.name}팀"'Django' 카테고리의 다른 글
Django Redis cache (1) 2023.01.03 Django timezone (0) 2022.10.14 Django UniqueConstraint (0) 2022.10.07 Django Signal (1) 2022.09.29 Django unittest pytest (0) 2022.09.28