Django中访问QuerySet嵌套字段实现购物车按商家分组下单
Django购物车按商家分组生成订单性能优化方案
问题根因
- N+1查询问题:你查询
Cart_Item时没有提前加载嵌套关联的外键数据,每次循环访问商品对应的商家ID时,都会触发新的数据库查询,商品数量越多查询次数越高,性能损耗严重。 - 代码逻辑错误:
- 过滤条件错误:
Cart_Item的userID字段关联的是Cart表,你直接传入request.user(User实例)作为过滤条件会抛出匹配类型错误。 - 错误调用QuerySet方法:
item是单个Cart_Item实例,不能调用仅QuerySet可用的values()方法,会直接报错。
- 过滤条件错误:
优化方案
1. 优化数据库查询
使用select_related将Cart_Item关联的Inventory、Inventory关联的Producer数据一次性通过JOIN查询取出,完全避免循环中的重复查询:
# 先获取当前用户对应的购物车实例 user_cart = Cart.objects.get(userID=request.user) # 一次性加载所有需要的关联字段,同时按商家ID排序方便分组 cart_items = Cart_Item.objects.select_for_update().filter( userID=user_cart ).select_related( "item_id", "item_id__producerID" ).order_by("item_id__producerID")
2. 高效分组生成订单
直接取外键缓存的主键值做分组比对,不需要序列化实例,也不需要加载完整的Producer对象:
from rest_framework import status from rest_framework.response import Response from django.db import transaction class PlaceOrderView(generics.CreateAPIView): def post(self, request, *args, **kwargs): user_cart = Cart.objects.get(userID=request.user) cart_items = Cart_Item.objects.select_for_update().filter( userID=user_cart ).select_related( "item_id", "item_id__producerID" ).order_by("item_id__producerID") if not cart_items: return Response({"msg": "购物车为空"}, status=status.HTTP_400_BAD_REQUEST) current_producer_id = None current_items = [] for item in cart_items: # 直接取外键存储的商家ID,无需加载完整Producer实例,性能更高 producer_id = item.item_id.producerID_id if not current_producer_id: current_producer_id = producer_id current_items.append(item) continue if producer_id == current_producer_id: current_items.append(item) else: # 提交上一个商家的订单 self.placeOrder(current_producer_id, current_items) current_producer_id = producer_id current_items = [item] # 提交最后一组订单 self.placeOrder(current_producer_id, current_items) return Response({"msg": "订单生成完成"}, status=status.HTTP_201_CREATED) def placeOrder(self, producer_id, items): with transaction.atomic(): # 你的订单业务逻辑 pass
可选简化写法
可以用itertools.groupby直接完成分组,代码更简洁:
from itertools import groupby def post(self, request, *args, **kwargs): user_cart = Cart.objects.get(userID=request.user) cart_items = Cart_Item.objects.select_for_update().filter( userID=user_cart ).select_related( "item_id", "item_id__producerID" ).order_by("item_id__producerID") for producer_id, item_group in groupby(cart_items, key=lambda x: x.item_id.producerID_id): self.placeOrder(producer_id, list(item_group)) return Response({"msg": "订单生成完成"}, status=status.HTTP_201_CREATED)
内容的提问来源于stack exchange,提问作者Alicklaz
相关产品推荐
相关产品推荐

