Django ORM 사용할 때 Query 개수를 줄일 수 있는 방법 중 select_related, prefetch_related에 대하여 알아보자!😃
select_related
, prefetch_related
는
하나의 QuerySet을 가져올 때, 미리 related objects들까지 다 불러와주는 함수
query는 복잡하게 만들지만, 불러온 데이터들은 모두 캐시에 남아있게 되므로 DB에 다시 접근해야 하는 수고를 덜어줄 수 있다.😞
두 메서드 모두 DB에 접근하는 횟수, 즉 쿼리의 개수를 줄임으로써 성능향상을 기대할 수 있다!
출처 : Django Book(https://djangobook.com/)
언제 사용하는가?
어떻게 수행되는가?
형식
wish_list = WishList.objects.filter(user_id= 1 ).select_related('product', 'product__name')
언제 사용하는가?
어떻게 수행되는가?
형식
class Image(models.Model):
product = models.ForeignKey(Product, related_name = 'images')
# related_name에 따라
product.images.all()
ForeignKey를 가진 클래스에서 가지지 않는 클래스를 참조할 때는 정참조
ForeignKey를 가지지 않은 클래스에서 가진 클래스를 참조할 때는 역참조
class ExampleA(models.Model):
pass
class ExampleB(models.Model):
a = ForeignKey(ExampleA)
ExampleB.objects.select_related('a').all() # 정참조
ExampleA.objects.prefetch_related('exampleb_set').all() # 역참조
Product
의 모델
class ProductName(models.Model):
name = models.CharField(max_length = 200)
class Meta:
db_table = "product_names"
def __str__(self):
return self.name
class Product(models.Model):
name = models.ForeignKey(ProductName, on_delete = models.CASCADE)
description = models.CharField(max_length = 500)
price = models.IntegerField()
is_new = models.BooleanField(null = True)
sub_category = models.ForeignKey('SubCategory', on_delete = models.CASCADE)
product_color = models.ManyToManyField('Color', through = 'ProductColor')
product_url = models.URLField(max_length=2000, null=True)
class Meta:
db_table = "products"
class Color(models.Model):
name = models.CharField(max_length = 200)
color_image = models.URLField(max_length = 2000)
class Meta:
db_table = "colors"
def __str__(self):
return self.name
class ProductColor(models.Model):
product = models.ForeignKey(Product, on_delete = models.CASCADE)
color = models.ForeignKey(Color, on_delete = models.CASCADE)
class Meta:
db_table = "product_colors"
class Image(models.Model):
image_url = models.URLField(max_length = 2000)
product = models.ForeignKey('Product', on_delete = models.CASCADE, null = True)
class Meta:
db_table = "images"
def __str__(self):
return self.image_url
WishList
의 모델
class WishList(models.Model):
user = models.ForeignKey('User', on_delete = models.CASCADE)
product = models.ForeignKey('product.Product', on_delete = models.CASCADE)
count = models.IntegerField(default = 1, help_text = "제품개수")
class Meta:
db_table = "user_wishlists"
내가 해볼 것은 wishlist
에 있는 product
의 정보들을 가져오는 것이다.
이때 select_related
와 prefetch_related
를 사용하여야 Query 개수를 줄이면서 DB hit을 적게 할 수 있다!
우선 관계부터 정렬하여 보자.
wishlist
에서 product
와 product의 name
에 접근하기 위하여 select_related
,
many-to-many
관계인 product의 color
, product의 one-to-many관계
인 image
에 접근하기 위하여 prefetch_related
를 사용하였다!
코드는 다음과 같다!
wish_list = WishList.objects.filter(user_id=request.user.id)
.select_related('product', 'product__name')
.prefetch_related('product__product_color', 'product__image_set')
전체코드
class WishListView(View):
@login_required
def get(self, request):
wish_list = WishList.objects.filter(user_id=request.user.id)
.select_related('product', 'product__name')
.prefetch_related('product__product_color', 'product__image_set')
result = [{
"id": wish.product.id,
"name": wish.product.name.name,
"option": [color.name for color in wish.product.product_color.all()],
'img': [image.image_url for image in wish.product.image_set.all()],
"quantity": wish.count,
"price": wish.product.price
} for wish in wish_list]
return JsonResponse({'result' : result})
안녕하세요! 글 잘봤습니다. 혹시 글 내부에 삽입된 관계도 뭘로 그리셨을까요?