select related, prefetch related

hyeyul·2020년 5월 23일
0

Django

목록 보기
6/8

ORM,QuerySet이란?

ORM(Object-relational Mapping)은 데이터 베이스와 객체지향 프로그래밍 언어 간의 호환되지 않는 데이터를 변환하는 프로그래밍 기법이다.(from wiki) 간단히 말하면 SQL문을 몰라도 Django에서 지원하는 ORM을 통해 database에 접근해서 원하는 data를 가져오고, 저장도 할 수 있게 해주는 기법이다.

QuerySet(쿼리셋)은 ORM에서 django model objects를 통해 반환되는 객체다. (python data structure)

select_related는 쿼리셋을 반환할 때 foreign-key, OneTonOneFeild 관계인 모델들 함께 가져오는 ORM이다.

select_related는 좀 더 복잡한 쿼리가 되지만, foreign-key, OneTonOneFeild 관계인 모델들을 코드에서 재사용할 경우 추가적인 쿼리가 필요 없다. (캐싱되기 때문에)

prefetch_related는 쿼리셋을 반환할 때 foreign-key, OneTonOneFeild 관계뿐만 아니라 ManyToMany, ManyToOne 관계의 모델들 함께 가져오는 ORM이다.
selected_related는 하나의 Query로 related Objects들을 불러오지만 Prefetch_related는 main qeury가 실행이 된 후 별도의 query가 실행된다. 따라서 1번 이상 쿼리가 진행되기 때문에 가급적 select_related를 사용하는 것이 리소스 소모를 줄일 수 있다.

하지만 역참조일 경우 prefetch_related를 썼을 때와 안썼을 때를 비교하보면,

class Item(models.Model):
    name               = models.CharField(max_length = 50, null=True)
    nameEnglish        = models.CharField(max_length = 50, null=True)
    description        = models.CharField(max_length = 50, null=True)
    thumbnail          = models.CharField(max_length = 200, null=True)
    drink_category     = models.ForeignKey('DrinkCategories', on_delete = models.SET_NULL, null = True)
    nutrition          = models.ForeignKey('Nutrition', on_delete = models.SET_NULL, null = True)
    size               = models.ForeignKey('Size', on_delete = models.SET_NULL, null = True)
    allergy            = models.ManyToManyField('Allergy', through='ItemAllergy')
    class Meta:
        db_table = 'items'

class Size(models.Model):
    sizeName  = models.CharField(max_length = 50, null=True)
    sizeGauge = models.DecimalField(max_digits=5, decimal_places=2, null=True)
    class Meta:
        db_table = 'sizes'
- prefetch_related를 썼을 때와 안썼을 때
#1
In [255]: Size.objects.filter(id=3).prefetch_related('item_set')[0].item_set.values()                       
Out[255]: <QuerySet [{'id': 4, 'name': '프라푸치노', 'nameEnglish': 'frafchino', 'description': '맛있음', 'thumbnail': 'url@url.com', 'drink_category_id': 1, 'nutrition_id': 1, 'size_id': 3}]>
#2
In [256]: Size.objects.filter(id=3)[0].item_set.values()                                                    
Out[256]: <QuerySet [{'id': 4, 'name': '프라푸치노', 'nameEnglish': 'frafchino', 'description': '맛있음', 'thumbnail': 'url@url.com', 'drink_category_id': 1, 'nutrition_id': 1, 'size_id': 3}]>

위의 두가지경우는 '_set'을 통해서 역참조를 한 경우이다. 두가지 경우 다 id=3인 Size테이블의 객체를 바라보고있는 Item테이블의 객체의 values를 가져왔다.

그렇다면 prefetch_related는 왜 쓰는 걸까?

답은 쿼리를 줄이는데 있다. 앞서설명했듯이 prefetch_related와 select_related를 사용하면 참조관계에 있는 테이블을 캐싱한다고 했다.

prefetch_related를 사용하지않은 두번째 경우는 같은결과값을 가져왔지만 item_set 매서드로 Item 테이블로 acess했을 때 Item테이블을 캐싱하지 않았기 때문에 쿼리를 한번 더 추가한다.

prefetch_related를 사용해서 코드는 길어졌지만 쿼리의 효율성을 위해서 경우에 따라 추가해주는것이 좋을 것 같다

0개의 댓글