django get기능에서 orm을 통해서 데이터를 가져오는 몇가지 형태에 대해서 알아보자.
product_info = Product.objects.select_related('harvest_year', 'measure').order_by('id').values(
'name',
'id',
'price',
'small_image',
'harvest_year__year',
'measure_id__measure',
'is_on_sale',
'is_in_stock',
)
가장 간편하고 직관적인 방법이라고 할 수 있다. 찾고자하는 필드명을 values()안에 넣어준다. 정참조 관계일 때는 lookup을 사용해서 값을 쉽게 가져올 수 있다. 여러개의 객체를 가져올 때 사용한다. 하지만 values()안에 넣어준 필드명이 키값이 되기 때문에 lookup을 사용한 경우 키값을 정리 할 수 없다.
ManytoMany관계에 있는 필드와 OnetoOne의 테이블을 같이 가져올 경우
>>> Product.objects.values('similar_product','name')
<QuerySet [{'similar_product': 2, 'name': 'Asparagus'}, {'similar_product': 3, 'name': 'Asparagus'}, {'similar_product': 4, 'name': 'Asparagus'},
다음과 같이 many에 있는 객체의 숫자만큼 같은값이 반복되기 때문에 ManytoMany관계에있는 필드는 따로 값을 가져오는것이 값을 정리하기에 좋다.
data = [{
'title' : props.title,
'image_url' : props.background_image,
'detail_id' : props.id,
} for props in videos]
하나의 객체를 지정해서 하나의 value를 가져오기 때문에 key값을 원하는 값으로 지정할 수 있다.(프론트엔드와 협의해서 지정한다.)
[1] data_caching = Product.objects.select_related('measure', 'harvest_year').prefetch_related('similar_product').get(id=product_id)
product_info = {
'name' : data_caching.name,
'harvest_year_id__year' : data_caching.harvest_year.year,
'measure_id__measure' : data_caching.measure.measure,
'is_in_stock' : data_caching.is_in_stock,
'description' : data_caching.description,
'price' : data_caching.price,
'small_image' : data_caching.small_image,
'big_image1' : data_caching.big_image1,
'big_image2' : data_caching.big_image2,
'big_image3' : data_caching.big_image3,
'energy' : data_caching.energy,
'carbonydrate' : data_caching.carbonydrate,
'protein' : data_caching.protein,
'fat' : data_caching.fat,
'mineral' : data_caching.mineral,
'vitamin' : data_caching.vitamin,
[2] 'similar_product' : list(data_caching.similar_product.values('name', 'harvest_year_id__year', 'is_in_stock', 'measure_id__measure'))
}
우선 [1]에서와 같이 select_related를 통해서 OnetoOne관계의 테이블, prefetch_related를 통해서 ManytoMany관계의 테이블을 캐싱해준다. 하나의 객체를 가져올 것이기 때문에 get()을 사용해서 값을 가져오고 key값을 정해준다. 마지막으로 [2]에서 ManytoMany관계에 있는 값을 가져올 경우 여러개의 값을 values()로 가져오고 리스트로 만들어 딕셔너리의 value로 넣는다.
번들상품 특가 판매를 생각해보자. 하나의 번들프로모션읜 여러개의 상품을 가지고, 하나의 상품도 여러개의 번들프로모션에 포함될 수 있으므로 상품과 번들은 ManytoMany관계에 있다.
하나의 번들프로모션에는 여러개의 상품이 묶여져 있다. 그리고 하나의 번들프로모션에는 묶음 가격, 프로모션 이름, 행사기간 등의 데이터가 들어가 있을 것이다. 그렇다면 모든 번들프로모션의 데이터(묶음상품들, 묶음가격, 프로모션이름, 행사기간)를 어떻게 가져와서 정리 할 까?
묶음가격, 프로모션이름, 행사기간은 하나의 번들프로모션 당 하나의 값을 가진다. 즉 OnetoOne의 관계이다. 묶음상품들은 상품가격, 상품단위, 한유닛의 가격등의 값을 가지며 하나의 번들프로모션에 여러개의 단위가 포함 된다.
def get(self, request):
[1] data_caching = Bundle.objects.prefetch_related('product_set')
[2] bundle_info = data_caching.values('title', 'price', 'is_in_promotion')
[3] content_info = [
list(data.product_set.values('measure_id__measure', 'name')
.annotate(Count('name'))) for data in data_caching
]
[4] bundle=[data for data in zip(bundle_info, content_info)]
[1] 우선 Bundle 테이블과 ManytoMany관계에있는 Product테이블을 캐싱해준다.
[2] lookup을 사용할 필요가 없어 key값이 비교적 깔끔하기 때문에 Bundle 테이블의 값을 values로 가져온다.
[3] Product테이블의 값을 lookup으로 가져온다. 단위와 이름을 가져오는데, 하나의 상품이 번들에 여러개 포함 될 수 있기 때문에 상품테이블의 'name'의 갯수를 세서(.annotate(Count('name'))) 몇개가 있는지도 같이 가져온다. 그리고 그 값을 for문을 통해 list로 만들어준다.
>>> Bundle.objects.prefetch_related('product_set')[0].product_set.values('name').annotate(Count('name'))
<QuerySet [{'name': 'Raspberries', 'name__count': 2}, {'name': 'Strawberries', 'name__count': 2}]>
이덱스가 0인 번들상품과 manytomany관계에 있는 product테이블에 있는 객체의 'name'을 가져오고 그 'name'이 몇개인지 count해주는 함수를 사용한다. 즉, 0번째 번들프로모션에는 Raspberries가 2개가 들어있고 Strawberries가 2개가 들어있다.
[4] bundle_info와 content_info는 길이가 같기 때문에 zip을 사용해서 묶어준다.