You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django中访问QuerySet嵌套字段实现购物车按商家分组下单

Django购物车按商家分组生成订单性能优化方案

问题根因

  1. N+1查询问题:你查询Cart_Item时没有提前加载嵌套关联的外键数据,每次循环访问商品对应的商家ID时,都会触发新的数据库查询,商品数量越多查询次数越高,性能损耗严重。
  2. 代码逻辑错误:
    • 过滤条件错误: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.14 04:03:04