제품에서 필터별 정렬 기능을 구현하기 위하여,
order_by()
메서드를 이용하여 처리하였다.
products/models.py
...
class Product(models.Model):
name = models.CharField(max_length=45)
class Option(models.Model):
size = models.PositiveIntegerField()
price = models.DecimalField(max_digits = 12, decimal_places = 2)
product = models.ForeignKey('Product', on_delete = models.CASCADE)
모델링을 살펴보면,
한 제품이 여러 사이즈와 여러 가격을 갖고있기 때문에
product에 참조키를 가진 option 테이블을 따로 만들어주었다.
id | 제품명 |
---|---|
1 | 아워 크림 |
2 | 아워 샴푸 |
3 | 아워 로션 |
4 | 아워 스킨 |
id | size | price | product_id |
---|---|---|---|
1 | 100 | 8000 | 1 |
2 | 500 | 45000 | 1 |
3 | 100 | 10000 | 2 |
4 | 100 | 15000 | 3 |
5 | 100 | 20000 | 4 |
>>> Product.objects.all().order_by('option__price')
이렇게 정렬을 하게 되면 product를 바라보는 모든 price를 참고하여 정렬하게 된다.
아워 샴푸(10000)
- 아워 로션(15000)
- 아워 스킨(20000)
- 아워 크림(45000)
하지만, 가격이 여러 개인 것들 중 가장 낮은 가격을 제품 리스트에 표기를 하기로 하였다면
아워 샴푸(10000)
- 아워 로션(15000)
- 아워 스킨(20000)
- 아워 크림(8000)
??
위와 같은 방법으로 정렬하는 것에 어려움이 생긴다.
그래서 사용한 방법이 annotate
이다.
annotate
란, 필드 하나를 만들고 거기에 '어떤 내용' 을 채우게 만드는 것이다. 엑셀에서 컬럼 하나를 만드는 것과 같다고 보면 된다. 주로 Min
, Max
, Avg
, Count
등의 method와 함께 쓰인다.
다시 말해서, 아래의 테이블에
id | 제품명 |
---|---|
1 | 아워 크림 |
2 | 아워 샴푸 |
3 | 아워 로션 |
4 | 아워 스킨 |
id | 제품명 | 가격 |
---|---|---|
1 | 아워 크림 | 8000 |
2 | 아워 샴푸 | 10000 |
3 | 아워 로션 | 15000 |
4 | 아워 스킨 | 20000 |
가격
이라는 필드를 만들고, 제품을 가리키는 price들 중 가장 낮은 가격을 내용으로 채워놓는 것이다.
>>> from django.db.models import Min # 낮은 값을 찾아주는 django ORM 메서드
>>> products = Product.objects.all().annotate(price=Min('option__price'))
# 위와 같은 모양을 가진 테이블을 생성
>>> products.order_by('price') # price 필드를 기준으로 정렬
이렇게 하면,
원하는대로 아워 크림(8000)
- 아워 샴푸(10000)
- 아워 로션(15000)
- 아워 스킨(20000)
정렬이 된다.
vs aggregate?
annotate
는 계산용 컬럼을 하나 추가하는 것이라면,
aggregate
는 특정 컬럼 전체를 대상으로 계산하는 것과 같다. (합, 평균, 개수 등)예를 들어 위에서 만든
products
가격의 평균을 구하고 싶으면 아래와 같이 하면 된다.>> from django.db.models import Avg >> products.aggregate(avg=Avg('price')) {'avg': Decimal('13250.000000')}
참조
(http://raccoonyy.github.io/django-annotate-and-aggregate-like-as-excel/)
👍🏼👍🏼👍🏼👍🏼👍🏼