์ ์๊ธฐ - ์ ๋ฐ ์์
๋ด์ฉ์ ๊ธฐ์ตํด๋ผ / ๋จ์ ์์
์ ๋ฆฌ ์๋ฆฌ์ฆ
ํ ์ด๋ธ๊ณผ ํ ์ด๋ธ์ ๊ด๊ณ๊ฐ 1:N์ด๋ N:N์ด๋ ์ด๋ฐ ๊ตฌ๋ถ์ด ํ๋ก์ ํธ ์ค๊ณํ ๋๋ถํฐ ํ์ํ๋ค. ๋งค์ฐ ์ค์!!
์๋ฅผ ๋ค์ด ์ํ์ ๋ณด๋ฅผ ๋ด์ product ํ ์ด๋ธ์ด ํ๋ ์๋ค๊ณ ํ์. ์ํ ๋ฆฌ๋ทฐ๊ฐ ์จ์ง๋ ํ ์ด๋ธ๋ ์๋ค๋ฉด
์ด ํ ์ด๋ธ์ ORM์ ๋ฐ์ํ๋ ์ ๊ฐ Model์ด๋ค.
๋ชจ๋ธ์ด๋ผ๋ฉด, ํฐ๋ฏธ๋ shell์์ python manage.py shell
์ ๊ผญ ํ๊ณ ์์ํด์ผ ํ๋ค.
ํ๋ฆ ์ค๋ช
๋ฌด์กฐ๊ฑด ๋ชจ๋ธ ํด๋์ค๋ถํฐ ๋จผ์ ์์ฑ
๊ทธ ๋ค์์ ์ฐ๋๋ DB์ migrationํ๊ณ
post ์ฑ ์ค์นํ๊ณ ๊ธฐ๋ณธ CRUD ํ ์คํธ ํด๋ดค๋ค.
class Product(models.Model):
name = models.CharField(max_length=100)
description = models.TextField(blank=True, null=True) # blank ๋น๋ฌธ์์ด ํ์ฉ์ฌ๋ถ
price = models.PositiveIntegerField()
stock = models.PositiveIntegerField()
available = models.BooleanField(default=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.name
# 1:1 ๊ด๊ณ
class Discount(models.Model):
product = models.OneToOneField(Product, on_delete=models.CASCADE)
discount_percentage = models.FloatField()
start_date = models.DateTimeField()
end_date = models.DateTimeField()
def __str__(self):
return f'{self.product.name} {self.discount_percentage * 100}% discount'
# 1:N ๊ด๊ณ
class Review(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE)
user_id = models.PositiveIntegerField(blank=True, null=True)
rating = models.PositiveIntegerField(default=1, help_text="ํ์ 1 ~ 5")
comment = models.TextField(blank=True)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f'{self.product.name} ์ํ๋ฆฌ๋ทฐ by {self.user_id}'
# N:M ๊ด๊ณ
class Category(models.Model):
name = models.CharField(max_length=100, unique=True) # UQ ์ ์ฝ์กฐ๊ฑด ์ถ๊ฐ
products = models.ManyToManyField(Product, blank=True)
def __str__(self):
return self.name
product.sql
์ ์ python manage.py makemigrations product
python manage.py migrate product
๋ธ๋ฆฟ์ง ํ ์ด๋ธ์ด ์๋์ผ๋ก ํ์ฑ๋๋ค.
from product.models import Product, Discount, Review, Category
ํด๋๊ณ ์์ํ๊ธฐ
Review.objects.filter(product_id=1)
product_id๊ฐ 1์ธ ์ด์ ์กฐํํด์ review๋ฅผ ํํฐํด์ ๋ณด๊ฒ ๋ค. ์ฌ๊ธฐ์ object๋ Django์ ORM (Object-Relational Mapping)์์ ๊ธฐ๋ณธ์ ์ผ๋ก ์ ๊ณต๋๋ ๋งค๋์ (manager)๋ฅผ ์๋ฏธํ๋ค. ์ฆ, ๋ชจ๋ธ ํด๋์ค(Review)์ ์ฐ๊ฒฐ๋ ๊ธฐ๋ณธ ๋งค๋์ ์ญํ ์ ํ๋ค.
๐ฅ filter ๊ฐ์ ๊ธฐ๋ฅ์ ์ฐ๋ ค๋ฉด ํญ์ object ์ ๊ณ ์์ํด์ผ ํ๋ ๊ฑด๊ฐ? -> ๊ทธ๋ ๋ค.
๋ณ์์ ๋ฆฌ๋ทฐ๋ด์ฉ์ ๋ด๊ณ , ๋ค๋ฅธ ๋ด์ฉ๋ค๊ณผ ๊ฐ์ด ๋ฐ๋ณต๋ฌธ ๋๋ ค์ ๋ฐํํ๋ ๋ฐฉ๋ฒ๋ ์๋ค.
โ๏ธ์ด๊ฑฐ ๋ค ์ฐ๊ณ Enter ํ๋ฒ ๋ ์ณ์ค์ผ ๊ฒฐ๊ณผ๊ฐ ๋์จ๋ค.
๐ฅ ์ prodcut=product์ธ๊ฑฐ์ง..? -> ํ๋ / ๊ฐ์ฒด๋ค. ์๋๋ FK?๊ฐ ํ๋์์ id๊ฐ ๋๋๋ฐ ์ฌ๊ธฐ์ ์๋์ผ๋ก ๋ผ์ ์ด๋ ๊ฒ ๋๋ค..?
from django.db.models import Sum, Avg, Count, Max, Min
ํด๋๊ณ ์์ํ๊ธฐ
set.aggregate
annotate
: ํน์ ์ปฌ๋ผ์ ์ถ๊ฐํ๊ฑฐ๋, ์ง๊ณ
from datetime import datetime, timedelta
ํด๋๊ณ ์์
weeks๋ 1์ธ๋ฐ days๋ ์ 30์ธ๊ฑฐ์ง๐ฅ -> ๋จ์๋ค. days=30์ 30์ผ ๋จ์๋ค. weeks=1์ ์ผ์ฃผ์ผ์ด๋ค. timedelta ์์ฑ์์ ํ๋ผ๋ฏธํฐ
product.discount๋ฅผ ์ฐธ์กฐํ๊ณ ์๋ discount
1:1 ๊ด๊ณ
n๊ฐ๊ฐ ๋์์ ์๋ฌ
product์ discount ์์ฑ ์์ด์ ์ค๋ฅ
์์ฑ์ด ์๋์ง ์ฐพ์๋ณด๊ณ ์งํํด์ผ ํจ
์ค๋ฅ ์์ *100 ์ง์ฐ๊ณ ์งํ
- ์ฌ๋ฌ ์ ๋ณด ํ๋ฒ์ ๊ฐ์ด ๋ณด๊ธฐ
์๋ฌ
ํด๊ฒฐ -> tab์ด ์์๋ค.
products = Product.objects.all() ์ด๊ฑด product๋ง ๊ฐ๋ฆฌํจ๋ค. ์ discount๋ ์ ๋์ฌ๊น? ์ด๋ฐ ๊ฑธ lazy query ๋ผ๊ณ ํ๋๋ฐ
lazy query : ์ค์ ๋ก discount์ ๊ฐ์ ์กฐํ๋ฅผ ํด์ผ ํ ํ ๋ฐ ์ง์ง ํ์ํ ๋ํ๋ค. ์ค์ ๊ทธ ์์ฑ์ ์ ๊ทผํ ๋.
์ผ๋จ ์ด๋ ๊ฒ ํ๋ฉด ์ ํ+ํ ์ธ์ ๋ณด๋ ๋์จ๋ค.
์ต๊ทผ 10๊ฐ ์ฟผ๋ฆฌ๋ฅผ ์กฐํํด๋ณด๋ฉด
๊ณ์ ๋ฐ๋ณต๋๋ฉด์ ๋์จ๋ค.
๋ถํ์ํ๊ฒ ์ฟผ๋ฆฌ๊ฐ ๋ง์ด ๋ ์๊ฐ๋ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด์ ํ๋ฐฉ์ ๋ถ๋ฌ์ค๋ ๋ฐฉ๋ฒ์ด ์๋ค.
N+1 ๋ฌธ์ ๋ฅผ ํด๊ฒฐ๊ฐ๋ฅํ ์กฐ์ธ์ฟผ๋ฆฌ ์ฒ๋ฆฌ
discount์ ์ฐ๊ฒฐ๋ ๊ฒ๋ค์ ๋ฏธ๋ฆฌ ์ข ์กฐํํด ์ฃผ์ธ์. ๋ผ๋ ๋ป.
prefetch_related๋ฅผ ์ธ ๋ ์ด๋ฏธ ์ฟผ๋ฆฌ๊ฐ ๋ ์๊ฐ๋ค.
์๋ ๋ฐ๋ณต๋ฌธ์ด ์๋๋ผ, ์์์ ์ด๋ฏธ ์ฟผ๋ฆฌ๊ฐ ๋ ์๊ฐ๊ธฐ ๋๋ฌธ์, ์ฑ๋ฅ์ ์ผ๋ก ์ฐ์ํ๋ค.
์ฟผ๋ฆฌ ์กฐํ (์กฐ์ธ๋ ๊ฑธ๋ก ์ ๋์์ ๋์ค์ ๋ค์ ์๋ ค์ฃผ์ ๋ค๊ณ ํ์ฌ)
N:M ๊ด๊ณ ํ์ธํ๊ธฐ
- ํ๊ฐ์ ์ํ์ด ์ฌ๋ฌ๊ฐ์ ์นดํ ๊ณ ๋ฆฌ๋ฅผ ๊ฐ์ง๋ค.
- ํ๋์ ์นดํ ๊ณ ๋ฆฌ๊ฐ ์ฌ๋ฌ ์ํ์ ์ฌ์ฉ๋๋ค.
์ ๋ง์ด ๋๋ค๋ฉด, n:m ๊ด๊ณ๋ค.
Category์ Product์ ๊ด๊ณ์์ models.ManyToManyField๋ฅผ ์ฌ์ฉํ๊ฒ ๋ค.
n:m์์ products ์๋ฆฌ์๋ ๋ณดํต ๋ณต์ํ์ ์ด๋ค.
์นดํ
๊ณ ๋ฆฌ๊ฐ ํ๋ ์๋ id=1 ์ํ๊ณผ
์นดํ
๊ณ ๋ฆฌ๊ฐ ์ฌ๋ฌ๊ฐ ์๋ id=9 ์ํ
๊ฐ์ ์ ํด๋นํ๋ ์ํ์ ์กฐํํด๋ณด์
๐ฅ์กฐํํ ๋ ๋ชจ๋ธ์์ ์คฌ๋ products๋ฅผ ์จ์ค์ผ ํจ. ์ด์ ๋ ์ ๋ชจ๋ฅด๊ฒ ์. ์นดํ ๊ณ ๋ฆฌ์์ ๋ณด๋ ๊ฒ products์ฌ์ ๊ทธ๋ ๋ค..? -> categort์ prodcuts:ManyToManyField๋ฅผ ์ฐธ์กฐํ๊ฑฐ๊ณ Product์์๋ category set์ ๋ณด๋ธ๋ค. ์กฐํ๋ Category์์ ํ๋ ๊ฑฐ๋ผ์ products๋ผ๊ณ ์ฐ๋ ๊ฑฐ๋ค.
์ฐ๊ด๊ด๊ณ
1:N > N์ชฝ ๋ชจ๋ธ์ foreignKey ํ๋ ์ง์
1:1 > 1์ชฝ ๋ชจ๋ธ์ OneToOneField ์ง์
N:M > M์ชฝ ๋ชจ๋ธ์ ๊ฐ์ ManyToNanyField ์ง์
์ญ๋ฐฉํฅ ์ฐธ์กฐ ์ด๋ฆ์ related_name= ์ผ๋ก ๋ฃ๋๋ค.
๋ชจ๋ธ์ ๊ฐ์ ๋ฃ์ด์ฃผ์.
์๋๋ ๊ธฐ๋ณธ๊ฐ์ด review_set์ด์ง๋ง ํ๋ฌ๋ํธ๊ฐ ๋ฆฌ๋ทฐ ์ฐธ์กฐํ ๋ ์ฐ๋ ๊ฑด๋ฐ, ๋ณต์ํ์ผ๋ก ์ฐ๋ ๊ฒฝ์ฐ๊ฐ ๋ง๋ค.
์ฌ๊ธฐ๋ ์๋ category_set์ธ๋ฐ ๋ณต์ํ์ผ๋ก ์จ์ฃผ์.
aggregate ํจ์๋ฅผ ์ฐ๋ฉด ์ ์ฒด๋ฅผ ํ๋์ ๊ทธ๋ฃน์ผ๋ก ๊ฐ์ฃผํ๊ณ ์ฌ์ฉํ๋ ๊ฑฐ๋ค.
๊ทธ๋์ ์ ์ฒด ์ํ์ด ๋ช ๊ฑด์ด์ผ?
์ฟผ๋ฆฌ select cont(*) from product์ ๋๊ฐ๋ค.
๐ฅcount๊ฐ ๋ฐํ๋ ๋์
๋๋ฆฌ์ ํค๊ฐ์ด ๋๋ค?
์ํ์ ํ๊ท , ์ต๋๊ฐ, ์ต์๊ฐ ์กฐํ ๊ฐ๋ฅ
aggregate๋ฅผ ์ฐ๋ฉด ๋๋ค. ์ ์ฒด๋ฅผ ํ๋์ ๊ทธ๋ฃน์ผ๋ก ๋ฌถ์ด์ฃผ๋๊น.
๋ ๋ค ๋๊ฐ๋ค.
groupby์ ํด๋นํ๋ ๊ฑด ์๋ค. value๋ผ๊ณ ์๋๋ฐ ์ด๊ฑธ๋ก ํ๋ฒ ํด๋ณด์.
annotate : ์ปฌ๋ผ์ ์ถ๊ฐํ๋ ์ .
์ํ๋ ์ปฌ๋ผ๋ง ์ถ๋ ค๋ด์ ๋์
๋๋ฆฌ๋ก ๋ฐํํด์ค๋ค.
๋ฆฌ๋ทฐ ํ๋ ํ๋์์ ์ํ๋ ์์๋ง ์ถ๋ ค๋ด์ ๋ณผ ์ ์๋ค.
๋ฆฌ๋ทฐ์ธ๋ฐ product id๋ง ์ ํ์๊ณ ๊ฐ์ ์ ๋ค๋ผ๋ฆฌ ๋ฌถ์ด๋ผ ํ ์ ์๋ค. ์ ํ๋ณ ๋ฆฌ๋ทฐ๊ฐ์ ๊ฐ์ ๊ฑธ ํ ์ ์๋ค.
์ง๊ธ ๋ฆฌ๋ทฐ์์ ์ํ๋ณ๋ก ์นด์ดํธํ์ง๋ง, ์ด๊ฑธ product์ ๊ฐ์ id๋ง ๋ฝ๊ณ , annotate๋ก ๋ฆฌ๋ทฐ์์ ๋ฆฌ๋ทฐ ์นด์ดํธ๋ฅผ ๋ถ๋ฌ๋ด๋ฉด ๋๋ค.
๋ฐ๋ณต๋ฌธ์ผ๋ก ์ ๋ณด์ด๊ฒ ํ๊ธฐ