Django

Django ORM (심화)

zhelddustmq 2024. 8. 30. 11:23

0. ORM사용하기 위한 사전 작업

 

articles/models.py

from django.db import models
from django.core.validators import MinValueValidator, MaxValueValidator

class Product(models.Model):
    CATEGORY_CHOICES = (
        ("F", "Fruit"),
        ("V", "Vegetable"),
        ("M", "Meat"),
        ("O", "Other"),
    )

    name = models.CharField(max_length=100)
    price = models.PositiveIntegerField()
    quantity = models.PositiveIntegerField()
    category = models.CharField(max_length=1, choices=CATEGORY_CHOICES)

    def __str__(self):
        return self.name

 

django seed로 생성

python manage.py seed products --number=30

 

 

Django ORM   (심화)

  • 기본적인 ORM 외에도 다양한 비지니스 상황에 맞는 쿼리가 필요
  • Django의 ORM은 대부분의 쿼리가 가능하도록 설계됨
  • 값을 가져와서 Python으로 조작해도 되지만 Database에서 조작한 후 가져오는것이 필요한 경우가 있음

query문 작동 확인은 shell_plus에서!

 

01. Q() 를 이용해서 여러 조건 연결하기

  • 여러 조건에 해당하는 ORM이 필요하다면 Q 객체를 사용
  • Q 는 조건을 정의하는 객체로 논리적 OR 조건을 만들거나 여러개의 조건을 결합해서 SQL의 WHERE 절에 해당하는 기능을 온전히 활용할 수 있음
  • & : and, | : or, ~ : not 연산자 활용 가능

 

# 가격이 15000보다 크거나 수량이 3000보다 적은 제품들을 조회
Product.objects.filter(
	Q(price__gt=15000) | Q(quantity__lt=3000)
)

# 가격이 10000보다 크고 수량이 2000보다 적은 제품들을 조회
Product.objects.filter(
	Q(price__gt=10000) & Q(quantity__lt=2000)
)

 

02. F() 를 이용해서 필드값 가져오기

  • F() 는 쿼리를 작성할 때 이전의 필드 값에 의존하는 작업을 도와줌
  • 주로 필드의 값을 가져오거나 업데이트해서 값을 참조하는데 사용
  • python 메모리로 값을 가져오지 않고 필요한 작업을 데이터베이스에서 수행할 수 있음
# 모든 프로덕트의 가격을 1000원 인상
Product.objects.update(price = F('price') + 1000)

 

03. annotate() 사용해서 추가 정보 제공하기

https://docs.djangoproject.com/en/4.2/ref/models/querysets/#annotate

 

QuerySet API reference | Django documentation

The web framework for perfectionists with deadlines.

docs.djangoproject.com

  • annotate == 주석을달다
  • 조회하는 쿼리셋 각각에 추가적인 정보(데이터)를 제공하는데 사용
# 각 프로덕트 별 total_price를 추가로 구성해서 조회(total_price = price * quantity)
products = Product.objects.annotate(
    total_price=F('price') * F('quantity')
)

 

03.aggregate() 를 사용해서 쿼리하기

  • aggregate == 종합/집약하다
  • 조회하는 쿼리셋 전체에 대해 결과를 집계/집약
  • 주로 집계 함수(Avg, Sum, Count 등)와 많이 사용
# 전체 프로덕트의 평균 가격
Product.objects.aggregate(Avg('price'))
# {'price__avg': 14627.76}

# 컬럼 이름을 주고 싶을 때
Product.objects.aggregate(my_avg = Avg('price'))
# {'my_avg': 14627.76}

 

 

 

04. Group By 적용하기

# 이렇게 쓰면 전체 데이터 조회랑 같음
Product.objects.aggregate(Count('category'))
# {'category__count': 30}

 

따라서 세가지 방법이 있음.

-  (Recommend) Django에서 Group By는 아래의 두 단계를 이용해서 수행

# 내가 원하는 컬럼만 뽑기
Product.objects.values('category')
# <QuerySet [{'category': 'M'}, {'category': 'O'}, 
# {'category': 'V'}, {'category': 'M'}, ...
# {'category': 'F'}, '...(remaining elements truncated)...']>


# annotate() 로 묶어서 Group By를 수행
Product.objects.values('category').annotate(category_count = Count('category'))
# {'category': 'F', 'category__count': 15}, 
# {'category': 'M', 'category__count': 15}, 
# {'category': 'O', 'category__count': 15}, 
# {'category': 'V', 'category__count': 5}

 

- Raw()를 이용해서 직접 SQL문을 입력하기 (Django ORM으로 하기 어려운 쿼리문이라면)

categories_count = Product.objects.raw(
'''
SELECT "id", "category", COUNT("category") AS "category_count" 
FROM "products_product" 
GROUP BY "category"
'''
)

for each in categories_count:
	print(each.category_count, each.category)

# 15 F
# 15 M
# 15 O
# 5 V

 

- 직접 Database Connection을 만들고 쿼리하기 (마찬가지로 Django ORM으로 하기 어려운 쿼리문이라면)

from django.db import connection

sql_query = '''
SELECT "category", COUNT("category") AS "category_count" 
FROM "products_product" 
GROUP BY "category"
'''
cursor = connection.cursor()
cursor.execute(sql_query)
result = cursor.fetchall()
print(result)

# [('F', 15), ('M', 15), ('O', 15), ('V', 5)]

 

작성하는 대부분의 쿼리는 Django ORM 선에서 해결됨으로 첫번째 방법을 사용하자!

'Django' 카테고리의 다른 글

Redis  (1) 2024.08.30
Django ORM 최적화  (0) 2024.08.30
JSON Web Token, JWT  (0) 2024.08.30
Serializer 활용하기  (0) 2024.08.29
Relationship과 DRF(with. particular seed)  (0) 2024.08.29