Django ORM QuerySet Method

김기현·2022년 2월 13일
0
post-thumbnail

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(다)

1) 변수를 drink로 넣은 경우입니다.
>>> 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로 넣은 경우입니다.

다대다 관계이기 때문에 반대의 경우로 참조하는 것도 가능합니다. 알러지 항목이 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로 지정해주지 않으면 오류가 나타나니 꼭 지정해줍니다.

profile
피자, 코드, 커피를 사랑하는 피코커

0개의 댓글