Model method(또는 QuerySet method)를 통하여 데이터베이스에 내장된 데이터를 관리하는 기능을 사용할 수 있습니다.
우선, models.py의 코드는 다음과 같습니다.
from django.db import models
class Drink(models.Model):
category = models.ForeignKey(
"Category", related_name="drinks", on_delete=models.CASCADE, null=True
)
korean_name = models.CharField(max_length=45)
english_name = models.CharField(max_length=45)
description = models.TextField(max_length=300)
class Meta:
db_table = "drinks"
class Allergy(models.Model):
name = models.CharField(max_length=45)
drinks = models.ManyToManyField(
Drink, related_name="allergies", through="AllergyDrink"
)
class Meta:
db_table = "allergies"
class Category(models.Model):
menu = models.ForeignKey(
"Menu", related_name="categories", on_delete=models.CASCADE, null=True
)
name = models.CharField(max_length=45)
class Meta:
db_table = "categories"
class Menu(models.Model):
name = models.CharField(max_length=45)
class Meta:
db_table = "menus"
class Nutrition(models.Model):
one_serving_kcal = models.DecimalField(max_digits=10, decimal_places=2, null=True)
sodium_mg = models.DecimalField(max_digits=10, decimal_places=2, null=True)
saturated_fat_g = models.DecimalField(max_digits=10, decimal_places=2, null=True)
sugars_g = models.DecimalField(max_digits=10, decimal_places=2, null=True)
protein_g = models.DecimalField(max_digits=10, decimal_places=2, null=True)
caffeine_mg = models.DecimalField(max_digits=10, decimal_places=2, null=True)
drink = models.ForeignKey(
"Drink", related_name="nutritions", on_delete=models.CASCADE, null=True
)
size = models.ForeignKey(
"Size", related_name="nutritions", on_delete=models.CASCADE, null=True
)
class Meta:
db_table = "nuritions"
class Image(models.Model):
image_url = models.CharField(max_length=2000)
drink = models.ForeignKey(
"Drink", related_name="images", on_delete=models.CASCADE, null=True
)
class Meta:
db_table = "images"
class AllergyDrink(models.Model):
allergy = models.ForeignKey(
"Allergy", related_name="allergydrinks", on_delete=models.CASCADE, null=True
)
drink = models.ForeignKey(
"Drink", related_name="allergydrinks", on_delete=models.CASCADE, null=True
)
class Meta:
db_table = "allergy_drinks"
class Size(models.Model):
name = models.CharField(max_length=45)
size_ml = models.CharField(max_length=45, null=True)
size_fluid_ounce = models.CharField(max_length=45, null=True)
class Meta:
db_table = "sizes"
# # categories(다) - (일)menu : one-to-many : Menu 메뉴에 음료/푸드/상품 등 카테고리
# # drinks(다) - (일)categories : one-to-many
# # images (다) - drinks(일) : one-to-many
# # nutritions(다) - drinks(일) : one-to-many
# # nutritions(다) - sizes(일) : one-to-many
# drinks(다) - (다) allergy : many-to-many / allergy_drink 중간테이블
# NULL은 포인터가 가져올 값이 없는 상태
# 중간테이블 AllergyDrink를 직접 정의 by through mode
# 데이터는 임의로 넣었습니다.
1. 메뉴(일) - 카테고리(다)
>>> category = Category.objects.get(id=1)
>>> category
<Category: Category object (1)>
>>> category.name
'콜드브루'
>>> category.menu.name
'음료'
menu
는 Category 클래스 안의 menu를 지칭합니다. SQL을 보면 id값을 받기 때문에 menu_id
로 표현되어있습니다. 헷갈리지 않기!
1. 카테고리(다) - 메뉴(일)
카테고리가 메뉴를 정참조하고 메뉴가 카테고리를 역참조하는 구조입니다. menu변수에 객체 담아 메뉴id가 2에 해당하는 카테고리 중 id가 4인 카테고리를 역참조 해 가져옵니다.
>>> menu = Menu.objects.get(id=2)
>>> menu.name
'푸드'
id=2인 메뉴는 푸드입니다.
>>> menu.categories.get(id=4)
<Category: Category object (4)>
>>> menu.categories.get(id=4).name
'케이크'
Category 클래스에서 외래키 메뉴항목 중 related_name = categories
로 지정하고 id=4인 항목을 역참조한 것입니다.
아래의 코드는 filter로 역참조 객체를 가져온 것입니다.
<QuerySet [<Category: Category object (4)>]>
>>> menu.categories.filter(id=4).name
Traceback (most recent call last):
File "<console>", line 1, in <module>
AttributeError: 'QuerySet' object has no attribute 'name'
filter()
는 객체가 아닌 QuerySet을 반환하기에 .name을 했을 때 에러가 나옵니다. 따라서 [0]
을 붙여 .name으로 카테고리 이름을 확인합니다.
>>> menu.categories.filter(id=4)[0].name
'케이크'
1. Drinks(다) - AllergyDrink(중간테이블) - Allergy(다)
>>> drink = Drink.objects.get(id=5)
>>> drink.korean_name
'나이트로 바닐라 크림'
나이트로 바닐라 크림이 가지고 있는 알러지를 확인하기 위해 역참조를 합니다.
>>> drink.allergies.all()
<QuerySet [<Allergy: Allergy object (1)>, <Allergy: Allergy object (2)>]>
그리고 역참조된 항목을 d로 넣고 이름을 확인합니다.
>>> drink.allergies.filter()[0].name
'대두'
>>> drink.allergies.filter()[1].name
'우유'
drink와 연결된 특정값이 아닌 Allergy의 이름 모두를 꺼내고 싶을 때 for문을 사용합니다.
>>> for i in drink.allergies.all() : print(i.name)
...
대두
우유
다대다 관계이기 때문에 반대의 경우로 참조하는 것도 가능합니다. 알러지 항목이 2인 음료를 역참조하는 방법입니다.
>>> allergy = Allergy.objects.get(id=2)
>>> allergy
<Allergy: Allergy object (2)>
>>> allergy.name
'우유'
id가 2번, 7번이 알러지 id가 2(우유)인 항목들입니다.
>>> allergy.allergydrinks.get(id=2)
<AllergyDrink: AllergyDrink object (2)>
>>> allergy.allergydrinks.get(id=7)
<AllergyDrink: AllergyDrink object (7)>
>>> a = allergy.allergydrinks.get(id=2)
>>> b = allergy.allergydrinks.get(id=7)
>>> a.drink
<Drink: Drink object (5)>
>>> b.drink
<Drink: Drink object (13)>
>>> a.drink.korean_name
'나이트로 바닐라 크림'
>>> b.drink.korean_name
'밀당 초코 타르트'
이때 연결하는 테이블에 through
로 지정해주지 않으면 오류가 나타나니 꼭 지정해줍니다.