[TIL] select related, prefetch related

양희연·2020년 6월 25일
0

Django

목록 보기
9/14
post-thumbnail

🌕 select_related

Join 쿼리 (MySQL) 한번만 수행되고 참조하는 테이블 정보는 캐쉬되어 해당 테이블의 데이터 값을 가져올 때 DB 쿼리 안함

one to one, foreignkey 정참조의 경우에 사용

select_related('필드명')

from django.db import models

class Menu(models.Model):
    name = models.CharField(max_length = 50)

    def __str__(self):
        return self.name

    class Meta:
        db_table = 'menus'

class Category(models.Model):
    name = models.CharField(max_length = 50)
    menu = models.ForeignKey('Menu', on_delete = models.SET_NULL, null = True)

    def __str__(self):
        return self.name

    class Meta:
        db_table = 'categories'

👇 django shell 진입 👇

(project) $ ./manage.py shell
#categories 테이블에서 id가 1인 객체가 참조하고 있는 메뉴의 객체를 가져올 때

#select_related 사용하지 않고 데이터 가져올 시 hit 2번함
category = Category.objects.get(id=1)
category.menu
<Menu: drink>

#select_related 사용시 hit 한번만 함
category = Category.objects.select_related('menu').get(id=1)
category.menu
<Menu: drink>



🌎 prefetch_related

별도의 2개의 쿼리를 수행 후에 파이썬에서 조인한다.
역참조시 related_name을 사용해야 한다.

many to many, foreignkey 역참조 관계에서도 사용 가능하다.

prefetch_related('related_name')


> one to many (역참조)

from django.db import models

class Menu(models.Model):
    name = models.CharField(max_length = 50)

    def __str__(self):
        return self.name

    class Meta:
        db_table = 'menus'

class Category(models.Model):
    name = models.CharField(max_length = 50)
    menu = models.ForeignKey('Menu', on_delete = models.SET_NULL, null = True, related_name = 'category')

    def __str__(self):
        return self.name

    class Meta:
        db_table = 'categories'

👇 django shell 진입 👇

(project) $ ./manage.py shell
#menus 테이블에서 id가 1인 객체가 갖고 있는 category의 모든 데이터를 조회할 때

drink = Menu.objects.prefetch_related('category').get(id=1)
for category in drink.category.all():
    print(category.name)
'cold brew'
'espresso'

> many to many

class Drink(models.Model):
    name = models.CharField(max_length = 50)
    category = models.ForeignKey('Category', on_delete = models.SET_NULL, null = True)
    allergy = models.ManyToManyField('Allergy', through = 'AllergyDrink')

    def __str__(self):
        return self.name

    class Meta:
        db_table = 'drinks'

class Allergy(models.Model):
    name = models.CharField(max_length = 50)

    def __str__(self):
        return self.name

    class Meta:
        db_table = 'allergies'

class AllergyDrink(models.Model):
    allergy = models.ForeignKey('Allergy', on_delete = models.CASCADE)
    drink = models.ForeignKey('Drink', on_delete = models.CASCADE)

    class Meta:
        db_table = 'allergies_drinks'

👇 django shell 진입 👇

(project) $ ./manage.py shell
#알러지가 우유인 모든 음료를 조회하고 싶을 때

#prefetch_related 사용하지 않을 때
allergy = Allergy.objects.get(name = 'milk')
allergy.drink_set.all()
<QuerySet [<Drink: cold brew>, <Drink: macchiato>]>

#prefetch_related 사용할 때
allergy_id = Allergy.objects.get(name = 'milk').id
drinks = Drink.objects.filter(allergydrink__allergy=allergy_id).prefetch_related('allergydrink_set__allergy')
for drink in drinks:
    print(drink.name)
'cold brew'
'macchiato'
  • filter(allergydrink__allergy = allergy_id)
    filter 조건에서 _set은 쓰지 않는다.
    값은 숫자여야 한다.
profile
꾸준히 나아가자!

0개의 댓글