아래에서 사용된 코드는 아래 레포지토리에서 받을 수 있다.
https://github.com/junha6316/django_ex
장고 ORM의 기능 중 Aggregate와 Annotate에 대해 정리한 글이다. 어떤 기능을 하고 있는지는 많이 들어 알고 있지만 실제로 사용해본적은 없다;
http://raccoonyy.github.io/django-annotate-and-aggregate-like-as-excel/
아래 블로그를 참고해 작성했다.
이런 상황을 가정해보자
준하네 마트에는 모든 것을 판다. 그리고 초울트라특급 기술이 적용된 자동 판매 인식기 덕분에 언제 어떤 물건이 팔렸는지에 대한 로그가 데이터 베이스에 자동으로 저장된다. 하지만 이 마트에는 없는게 하나 있는데 바로 엑셀이다. 그덕에 데이터는 많지만 어떤 날에 어떤 물건이 얼마나 팔렸는지 매출이 얼마인지 알기 위해서 준하는 컴퓨터 화면을 보면서 24시간 주판을 두드려 계산했다. 주판을 두드리던 준하앞에 제이미 폭스가 쌍권총을 들고 나타나 이 문제를 해결해주겠다고 한다. 제이미가 어떻게 이 문제를 해결하는지 보자
사용한 모델은 Product와 OrderLog이며 아래와 같다. OrderLog는 판매데이터로 product를 외래키로 참조한다.
class Product(TimeStampedModel):
name = models.CharField("이름", max_length=150, unique=True)
price = models.IntegerField("가격")
def __str__(self):
return self.name
class OrderLog(models.Model):
created = models.DateTimeField()
product = models.ForeignKey('products.Product', related_name='order_log', on_delete=models.CASCADE)
1. 데이터 불러오기
먼저 제이미는 필요한 데이터만 가져오는 방법을 알려줬다.
"데이터는 아래처럼 불러올 수 있고 Queryset객체를 반환한다. QuerySet객체는 iterate하는 시점에서 딕셔너리로 반환된다.
물론 데이터를 불러올 수 있는 다양한 방법이 있지만 필요하지 않은 데이터를 갖고오거나 불필요한 쿼리를 날리는 것은 지양해야한다.
참고로 외래키로 참조되어 있는 필드를 불러오기 위해선 더블언더바(__)를 사용하면된다. OrderLog의 외래키 관계에 있는 product의 name을 갖고 오기 위해선 product__name로 적어서 갖고 오면된다. 장고"
logs = OrderLog.object.values(
'created', 'product__name', 'product__price'
)
#외래키로 참조되어 있는 필드를 불러오기 위해선 더블언더바(__)를 사용하면된다.
#order_log의 외래키 관계에 있는 product의 name을 갖고 오기 위해선 product__name
for log in logs:
#print(log.created) 이렇게 안됨
print(log['created']
위의 결과는 아래와 같다.
2. Annotate
준하는 제이미가 보여준 데이터를 보면서 너무 지저분하다고 생각했다. 그래서 물어봤다. 제이미 이거 데이터 좀 깨끗하게 못하나? 제이미는 쌍권총을 관자놀이에 대고 말했다.
"결과를 보면 외래키로 참조된 product__ 가 계속 나오는 것을 볼 수 있다. 이렇게 출력된다면 매번 log에서 어떤 값을 사용할 때마다 product__를 사용해야한다. 이건 불합리하다. 장고에서는 Annotate 기능을 이용하면 해결할 수 있다. 알겠니? 장고 "
from django.db.models import F
logs = OrderLog.objects.annotate(
name=F("product__name"),
price=F("product__price")
).values(
'created', 'name', 'price'
)
for log in logs:
print(log)
한결 깔끔하게 정리된걸 볼 수 있다.
사실 Annotate는 단순히 이름만 바꾸는 기능은 아니다. 이 부분은 뒤쪽 응용 부분에서 다루도록 하겠다.
3. Aggregate
제이미가 말했다.
"위에서 데이터를 가져왔다면 이제 이 데이터를 이용해 원하는 수치를 얻을 수 있다. 먼저 전체 판매 데이터의 총합을 알아보겠다. 총합은 aggregate를 사용하면된다. 알겠냐 장고?"
from django.db.models import Sum
#위에서 annotate된 QuerySet을 사용하면된다.
logs.aggregate(total_price=Sum('price'))
>>> {'total_price': 488000}
이번에는 날짜별 합을 가져와보자
from django.db.models import Sum
#위에서 annotate된 QuerySet을 사용하면된다.
daily_sum_list = logs.values(
'created'
). annotate(
daily_sum = Sum('price')
)
for data in daily_sum_list:
print(data)
다음은 날짜별로 어떤 제품이 몇개 팔렸는지 알아보자. 약간 Group By 느낌이라고 생각하면된다.
from django.db.models import Count
#위에서 annotate된 QuerySet을 사용하면된다.
product_cnt_list = logs.values(
'created','name'
). annotate(
product_cnt = Count('name')
)
for data in product_cnt_list:
print(data)
위 두개의 예제를 통해 values로 먼저 가져오고 annotate를 이용하면 된다는 것을 알 수 있다.
준하 마지막으로 특정 제품의 날짜별 판매 수량을 가져오려면 어떻게 해야할까?
from django.db.models import Count
#위에서 annotate된 QuerySet을 사용하면된다.
coupang_daily_cnt_list = qs.filter(
name="쿠팡"
).values(
'created','name'
). annotate(
product_cnt = Count('name')
)
for data in coupang_daily_cnt_list:
print(data)
제이미 덕에 준하는 더 효율적으로 데이터를 사용할 수 있게 되었고 부자가 되었다.
'Web Programming > Django' 카테고리의 다른 글
[pytest] Trouble Shooting (0) | 2021.11.06 |
---|---|
[DRF] Serializer Validation (0) | 2021.10.10 |
[Django] python manage.py를 하면 무슨 일이?(runserver 편) (0) | 2021.05.20 |
[장고백서] 필드에서 작성한 validator가 작동을 안할 때 (0) | 2021.04.09 |
[Django] Form 동적으로 생성하기 (0) | 2021.03.27 |