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

Django购物车添加同品异规格时随机触发KeyError 'product_obj'

Django购物车KeyError: 'product_obj'问题修复方案

问题背景

开发Django电商购物车时,支持添加同一Product的不同Size和Color规格商品,但随机触发KeyError: 'product_obj'异常,触发点在购物车的__iter__函数中。

错误根源

原代码存在两个核心问题:

  1. Product.objects.filter(id__in=product_ids)返回的查询集自动去重且无序,当购物车中有多个同产品不同规格的项时,product_ids会包含重复的product_id,但查询集仅返回一个该product对象。
  2. 通过index枚举products并对应cart.keys()[index]的匹配逻辑,无法保证每个cart item都能关联到product对象,导致部分item缺失product_obj属性,遍历计算total_price时触发KeyError。
  3. get_total_price方法直接遍历原始cart数据,而原始cart中不存在product_obj,同样会引发相同错误。

修复方案

核心思路

将查询到的products转换为以product.id为键的字典,通过cart item中的product_id直接匹配对应product对象,彻底摆脱顺序和去重问题的影响。

修正后的cart.py代码

from .models import Product

class Cart:
    def __init__(self, request):
        """Initialize the cart"""
        self.request = request
        self.session = request.session

        cart = self.session.get('cart')
        if not cart:
            cart = self.session['cart'] = {}
        self.cart = cart

    def add(self, product, size, color, quantity=1, replace_current_quantity=False):
        """Add a product to the cart"""
        flag = True
        # 遍历购物车项,匹配同产品同规格的项
        for key, item in self.cart.items():
            if item['product_id'] == product.id and item['size'] == size and item['color'] == color:
                if replace_current_quantity:
                    item['quantity'] = quantity
                else:
                    item['quantity'] += quantity
                flag = False
                break  # 找到匹配项后立即退出循环

        if flag:
            # 生成新的唯一键,简化原循环判断逻辑
            cart_item = str(max(map(int, self.cart.keys()), default=0) + 1)
            self.cart[cart_item] = {
                'product_id': product.id,
                'size': size,
                'color': color,
                'quantity': quantity
            }
        self.save()

    def remove(self, cart_item):
        """Remove a product from the cart"""
        cart_item = str(cart_item)
        if cart_item in self.cart:
            del self.cart[cart_item]
            self.save()
        
    def save(self):
        """Mark session as modified to save changes"""
        self.session.modified = True

    def __iter__(self):
        product_ids = [item['product_id'] for item in self.cart.values()]
        # 将products转为字典,通过product.id快速查找对应对象
        product_dict = {p.id: p for p in Product.objects.filter(id__in=product_ids)}

        cart = self.cart.copy()
        for key, item in cart.items():
            product = product_dict.get(item['product_id'])
            if product:
                item['product_obj'] = product
                item['cart_item'] = key
                item['total_price'] = product.price * item['quantity']
                yield item

    def __len__(self):
        return len(self.cart)
    
    def clear(self):
        del self.session['cart']
        self.save()

    def get_total_price(self):
        product_ids = [item['product_id'] for item in self.cart.values()]
        product_dict = {p.id: p for p in Product.objects.filter(id__in=product_ids)}
        
        total = 0
        for item in self.cart.values():
            product = product_dict.get(item['product_id'])
            if product:
                total += product.price * item['quantity']
        return total

额外优化

  • 简化了add方法中生成新购物车项键的逻辑,用max(map(int, self.cart.keys()), default=0) + 1替代循环判断,提升效率。
  • 在__iter__和get_total_price中增加if product:判断,避免因产品被删除等极端情况引发新的异常。

内容的提问来源于stack exchange,提问作者vahid sh

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 00:44:51