쇼핑몰을 보다보면 하나의 상품페이지에 여러개의 유사상품을 보여주는 경우를 만난적이 있을 것이다. 이 경우 django에서 어떻게 모델링을 해주어야 할까?
이 경우 상품 테이블에서 자기테이블과 many to many관계를 가지면 된다. 하나의 상품은 여러개의 유사상품을 가질 수 있기 때문이다.
class Product(models.Model):
name = models.CharField(max_length = 45, unique = True)
description = models.CharField(max_length=1000)
similar_product = models.ManyToManyField('self', through = 'SimilarProduct', symmetrical = False)
class SimilarProduct(models.Model):
from_product = models.ForeignKey('Product', on_delete = models.SET_NULL, null=True, related_name = 'from_product')
to_product = models.ForeignKey('Product', on_delete = models.SET_NULL, null=True, related_name = 'to_product')
class Meta:
unique_together = ('from_product', 'to_product')
db_table = 'similar_products'
Product테이블에 similar_product라는 필드를 'self' 즉, 자기자신을 참조하는 many to many관계설정을 해준다. 그리고 중간테이블(through)로 SimilarProduct테이블을 만들어준다. SimilarProduct테이블의 필드는 전부 Product테이블을 바라보고 유사한 상품의 여러 캐이스를 만들어낸다.
유사한 제품끼리의 관계를 만들었다면 값을 가져와보자.
In [5]: SimilarProduct.objects.create(from_product_id=1, to_product_id=2)
Out[5]: <SimilarProduct: SimilarProduct object (1)>
In [6]: SimilarProduct.objects.create(from_product_id=3, to_product_id=2)
Out[6]: <SimilarProduct: SimilarProduct object (2)>
In [7]: SimilarProduct.objects.create(from_product_id=1, to_product_id=1)
Out[7]: <SimilarProduct: SimilarProduct object (3)>
In [8]: SimilarProduct.objects.create(from_product_id=1, to_product_id=3)
Out[8]: <SimilarProduct: SimilarProduct object (4)>
우선 중간테이블에 제품끼리 몇가지 관계를 지어준다.
In [32]: Product.objects.prefetch_related('similar_product').values('name','similar_product__name')
Out[32]: <QuerySet [{'name': 'apple', 'similar_product__name': None}, {'name': 'banana', 'similar_product__name': 'banana'}, {'name': 'banana', 'similar_product__name': 'apple'}, {'name': 'banana', 'similar_product__name': 'coconut'}, {'name': 'coconut', 'similar_product__name': 'apple'}]>
prefetch_related를 사용해 중간테이블을 캐싱하고 배교의 주체가 되는 테이블의 이름(name필드)와 유사상품의 이름(similar_product__name)을 가져온다. 여기서 similar_product는 self many to many관계에 있기 때문에 lookup을 통해서 가져와야한다.(prefetcu_related로 중간테이블을 가져온 상태이기 때문이 쿼리는 한번으로 끝난다.)