Django购物车添加同品异规格时随机触发KeyError 'product_obj'
Django购物车KeyError: 'product_obj'问题修复方案
问题背景
开发Django电商购物车时,支持添加同一Product的不同Size和Color规格商品,但随机触发KeyError: 'product_obj'异常,触发点在购物车的__iter__函数中。
错误根源
原代码存在两个核心问题:
Product.objects.filter(id__in=product_ids)返回的查询集自动去重且无序,当购物车中有多个同产品不同规格的项时,product_ids会包含重复的product_id,但查询集仅返回一个该product对象。- 通过
index枚举products并对应cart.keys()[index]的匹配逻辑,无法保证每个cart item都能关联到product对象,导致部分item缺失product_obj属性,遍历计算total_price时触发KeyError。 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
相关产品推荐
相关产品推荐

