union 이후 filter 사용 시 에러 django.db.utils.NotSupportedError: Calling QuerySet.filter() after union() is not supported.
해결 방법 : union으로 가져온 모델들의 id로 다시 기존 모델에 접근해서 값 가져오기
https://bitcoden.com/answers/how-to-use-django-querysetunion-in-modeladminformfield_for_manytomany
def get_feeds(self):
#TODO 1. pet_type: DOG, cateogry 사료에 속하는 것 추출
product_dog_queryset = Product.objects.filter(pet_type='DOG')
# product_cat_queryset = Product.objects.filter(pet_type='CAT')
# product_all_queryset = Product.objects.filter(pet_type='ALL')
category_snack_queryset = Category.objects.filter(name=self.snack_for_dog['gum']).all().union(
*[Category.objects.filter(name=snack).all() for snack in list(self.snack_for_dog.values())[1:]]
)
product_prefetch_snack_for_dog = Prefetch(lookup='skus__product_options__product',
queryset=product_dog_queryset,
to_attr='product_snack_for_dog_list')
category_prefetch_snack_for_dog = Prefetch(lookup='skus__product_options__product__categories',
queryset=category_snack_queryset,
to_attr='category_snack_for_dog_list')
dog_snacks = get_read_queryset(IngredientBasedFeed).prefetch_related('skus',
'skus__product_options',
product_prefetch_snack_for_dog,
category_prefetch_snack_for_dog)
# print(ingredient_based_feeds[0].skus.all()[0].product_options.all()[0].product.categories.all()[0])
# print(connection.queries)
# dog_snacks 만 반환하면
# django.db.utils.NotSupportedError: Calling QuerySet.filter() after union() is not supported. 에러 발생함
feeds = get_read_queryset(IngredientBasedFeed).filter(id__in=dog_snacks)
return feeds
ingredient_type에 직접 할당할 수 없음
Direct assignment to the forward side of a many-to-many set is prohibited. Use ingredient_type.set() instead
ingredient_based_feed_recommendations.append(
IngredientBasedFeedRecommendation(
product=product,
ingredient_type=ingredient_types_dct['LOW_FAT']
)
)
ingredient_based_feed_recommendation = IngredientBasedFeedRecommendation.objects.create(
product=product,
)
ingredient_based_feed_recommendation.ingredient_type.add(ingredient_types_dct['LOW_PROTEIN'])
ingredient_based_feed_recommendations.append(
ingredient_based_feed_recommendation
)
prefetch_related는 불필요한 row들도 가져오는데, python단에서 join하니까 어쩔 수 없는건가? category부터 시작할 때 filter로 name을 특정해도 여러가지 category가 선택됨
# 거꾸로 category -> ingredientbasedfeed 까지 가는거 해보기
ingredient_based_feed_qs = get_read_queryset(IngredientBasedFeed)
ingredient_based_feed_prefetch = Prefetch(lookup='ingredient_based_feed', queryset=ingredient_based_feed_qs)
skus_qs = get_read_queryset(Sku).prefetch_related(ingredient_based_feed_prefetch)#.filter(id=18)
skus_prefetch = Prefetch(lookup='skus', queryset=skus_qs, to_attr='skus_list')
product_options_qs = get_read_queryset(ProductOption).prefetch_related(skus_prefetch)#.filter(id=19425)
product_options_prefetch = Prefetch(lookup='product_product_options', queryset=product_options_qs, to_attr='product_option_list')
products_qs = get_read_queryset(Product).prefetch_related(product_options_prefetch)
products_prefetch = Prefetch(lookup='products', queryset=products_qs, to_attr='product_list')
category_for_pet = self.category_for_pet['DOG']['snack']
category_queryset = get_read_queryset(Category).prefetch_related(products_prefetch).union(
*[get_read_queryset(Category).filter(name=c).all() for c in
list(category_for_pet.values())[8]]
)
코드
def ttest(self):
# prefetch_related에 Prefetch 사용해서 조건 걸어보기
# category_qs = get_read_queryset(Category).filter(name='저키/트릿')
# category_prefetch = Prefetch(lookup='categories', queryset=category_qs, to_attr='category_list')
#
# product_qs = get_read_queryset(Product).prefetch_related(category_prefetch)
# product_prefetch = Prefetch(lookup='product', queryset=product_qs, to_attr='product_list')
#
# product_options_qs = get_read_queryset(ProductOption).prefetch_related(product_prefetch).filter(id=19425)
# product_options_prefetch = Prefetch(lookup='product_options', queryset=product_options_qs, to_attr='product_option_list')
#
# skus_qs = get_read_queryset(Sku).prefetch_related(product_options_prefetch).filter(id=18)
# skus_prefetch = Prefetch(lookup='skus', queryset=skus_qs, to_attr='skus_list')
#
# TODO : 그냥 for문 돌면서 체크해야할듯 ㅠㅠㅠ
# feeds = get_read_queryset(IngredientBasedFeed).prefetch_related(skus_prefetch)
# skus = []
# product_options = []
# #
# for feed in feeds:
# if feed and feed.skus_list:
# # skus.append(feed.skus_list)
# for sku in feed.skus_list:
# if sku and sku.product_option_list:
# product_options.append(sku.product_option_list)
# print(f'with prefetch_related counts : {len(connection.queries)}')
# print(connection.queries)
# 거꾸로 category -> ingredientbasedfeed 까지 가는거 해보기
ingredient_based_feed_qs = get_read_queryset(IngredientBasedFeed)
ingredient_based_feed_prefetch = Prefetch(lookup='ingredient_based_feed', queryset=ingredient_based_feed_qs)
skus_qs = get_read_queryset(Sku).prefetch_related(ingredient_based_feed_prefetch)#.filter(id=18)
skus_prefetch = Prefetch(lookup='skus', queryset=skus_qs, to_attr='skus_list')
product_options_qs = get_read_queryset(ProductOption).prefetch_related(skus_prefetch)#.filter(id=19425)
product_options_prefetch = Prefetch(lookup='product_product_options', queryset=product_options_qs, to_attr='product_option_list')
products_qs = get_read_queryset(Product).prefetch_related(product_options_prefetch)
products_prefetch = Prefetch(lookup='products', queryset=products_qs, to_attr='product_list')
category_for_pet = self.category_for_pet['DOG']['snack']
category_queryset = get_read_queryset(Category).prefetch_related(products_prefetch).union(
*[get_read_queryset(Category).filter(name=c).all() for c in
list(category_for_pet.values())[8]]
)
# category_queryset.get(name='파우더'), category_queryset[0].product_list[0].product_product_options.all()[0].skus.all()[0].ingredient_based_feed.all()
return
union 사용 시 문제점 : django.db.utils.NotSupportedError: Calling QuerySet.filter() after union() is not supported.
or문은 느려서 union을 사용하려고 했다. 그러나 위 에러가 발생했다. 결과로 나온 값의 id를 다시 filtering해서 filter(id__in = qs)
하면 된다고 하지만 prefetch_related를 쓰면 inner join이 안되고 전체 row가 다 딸려와서 하는 id만 뽑아내기 어려웠다.
어쩔 수 없이 or문을 썼다. https://stackoverflow.com/questions/59495529/django-query-for-multiple-fields-with-list-values
no data e : Cannot filter a query once a slice has been taken.
해결 방법 안먹힘 https://stackoverflow.com/questions/24690755/django-prefetch-related-with-limit
→ no data e : (1235, "This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'") → product_ids를 list(product_ids)로 수정
product_ids = get_read_queryset(Product).all().order_by('-point').values_list('id', flat=True)[:300]
# product_prefetch = Prefetch(lookup='product', queryset=product_qs, to_attr='product_list')
ingredient_based_feed_recommendations = get_read_queryset(IngredientBasedFeedRecommendation).filter(
ingredient_types__id__in=ingredient_type_ids,
product__id__in=product_ids
).prefetch_related(
# product_prefetch,
feed_category_type_prefetch,
pet_type_prefetch,
)
Product 에서 관련 M:M 테이블을 prefetch_related로 가져오려고 하니까 불필요한 product도 가져오게 됨.
id를 직접 지정해서 id에 해당하는 product만 가져오도록 수정함
migration 파일 나누지 않으면 모델 rename 시 이전 의존성 migration 파일에서 rename된 모델 찾지 못해서 model resolve 문제 생김
모델 rename 작업과 foreignkey 추가 작업을 하나의 마이그레이션 파일에 했을 때 마주한 문제
마이그레이션 파일 나눠서 작업 함