[Django] annotate, aggregate

이태권 (Taekwon Lee)·2022년 6월 29일
0

Django

목록 보기
3/3

MySQL

아래에 임의로 하나의 영화에 여러 유저가 점수(score)를 매겼다.

mysql> select * from rates;
+----+-------+---------+---------+
| id | score | film_id | user_id |
+----+-------+---------+---------+
|  1 |   3.0 |       1 |       1 |
|  2 |   4.0 |       1 |       2 |
|  3 |   5.0 |       1 |       3 |
+----+-------+---------+---------+
3 rows in set (0.00 sec)

sum을 이용하여 기본적으로 점수의 총합을 구할 수는 있다.

In [9]: sum([rate.score for rate in Rate.objects.all()])
Out[9]: Decimal('12.0')

annotate

📝 annotate 개념

파이썬으로 갖고 올 때만 가상의 임시 필드를 생성한다.
즉, 임의로 속성을 만든다.

아래는 score라는 속성이 없다.

In [16]: film = Film.objects.get(id=1)
In [17]: dir(film)

Out[17]:
['DoesNotExist',
 'MultipleObjectsReturned',
...
 'save',
 'save_base',
 'serializable_value',
]

Sum과 Avg를 import

In [18]: from django.db.models import Sum, Avg

하지만 annotate를 통해 새로 score를 만들었다.

In [20]: film = Film.objects.annotate(score=Sum('rate')).get(id=1)

In [21]: dir(film)
Out[21]:
['DoesNotExist',
 'MultipleObjectsReturned',
...
 'save_base',
 'score',
 'serializable_value',
...
]

헷갈리니 다시 total_score로 바꾸어 보자.

In [22]: film = Film.objects.annotate(total_score=Sum('rate')).get(id=1)

In [23]: dir(film)
Out[23]:
['DoesNotExist',
 'MultipleObjectsReturned',
...
 'save_base',
 'serializable_value',
 'total_score',
 'unique_error_message',
 'validate_unique',
 'watchlist_set'

🖥 annotate를 활용한 총합 구하기

잘못된 예시 - id의 총합

In [26]: film = Film.objects.annotate(total_score=Sum('rate')).get(id=1)

In [27]: film.total_score
Out[27]: Decimal('6')

rate로만 접근을 하여 기본 값인 id의 총합이 생성 되었다.

모범 예시 - 평점 총합

In [28]: film = Film.objects.annotate(total_score=Sum('rate__score')).get(id=1)

In [29]: film.total_score
Out[29]: Decimal('12.0')

__를 통하여 ratescore에 대한 Sum 값을 구하였다.

🖥 annotate를 활용한 평균 구하기

In [32]: film = Film.objects.annotate(average_score=Avg('rate__score')).get(id=1)

In [33]: film.average_score
Out[33]: Decimal('4.00000')

In [34]: film = Film.objects.annotate(average_score=Avg('rate__score')).all()
In [35]: film[0].average_score
Out[35]: Decimal('4.00000')

In [36]: film[1].average_score

aggregate

📝 개념

필드가 아닌 값 하나만 갖고 온다 → 평점이라 해도, 값 하나만 갖고 온다

In [40]: Film.objects.aggregate(average_score=Avg('rate__score'))
Out[40]: {'average_score': Decimal('4.00000')}

In [41]: Film.objects.filter(id=1).aggregate(average_score=Avg('rate__score'))
Out[41]: {'average_score': Decimal('4.00000')}

In [42]: Film.objects.filter(id=2).aggregate(average_score=Avg('rate__score'))
Out[42]: {'average_score': None}

번외

배운 점

mysql> select * from rates;
+----+-------+---------+---------+
| id | score | film_id | user_id |
+----+-------+---------+---------+
|  1 |   3.0 |       1 |       1 |
|  2 |   4.0 |       1 |       2 |
|  3 |   5.0 |       1 |       2 |
+----+-------+---------+---------+
3 rows in set (0.00 sec)

실수로 값을 잘못 넣어 user_id번인 유저가 film_id에 대하여 2번 점수를 매겼다.

In [10]: a = Rate.objects.get(id=3)

In [11]: a.user_id=3

In [12]: a.save()

수정 완료!

mysql> select * from rates;
+----+-------+---------+---------+
| id | score | film_id | user_id |
+----+-------+---------+---------+
|  1 |   3.0 |       1 |       1 |
|  2 |   4.0 |       1 |       2 |
|  3 |   5.0 |       1 |       3 |
+----+-------+---------+---------+
3 rows in set (0.00 sec)
profile
(Backend Dev.) One step at a time

0개의 댓글