Ajax提交商品至购物车后,商品标题无法在购物车页显示
问题:购物车无法显示商品标题,数量和价格正常
我在跟着Very Academy的Zander做电商教程,现在到了用Ajax提交商品表单到购物车的环节。数量和价格都能正常传递显示,但商品标题就是出不来,其他功能都没问题。联系Zander一个月了没回复。
相关代码
Ajax代码
<script> $(document).on('click', '#add_button', function (e) { e.preventDefault(); $.ajax({ type: 'POST', url: '{% url "inventory:single_product_add_to_cart" %}', data: { productid: $('#add_button').val(), productqty: $('#select option:selected').text(), csrfmiddlewaretoken: "{{csrf_token}}", action: 'post' }, success: function (json) { }, error: function (xhr, errmsg, err) {} }); }) </script>
Product模型
class Product(models.Model): product_metas_set = models.ForeignKey(ProductMetas, on_delete=models.CASCADE, related_name='product_metas') PRODUCT_TYPE = ( ('single_product','Single product'), ('variable_product','Variable product'), ('groupped_product','Groupped product'), ) PRODUCT_STATUS = ( ('is_inactive','Inactive'), ('is_active','Active'), ('published','Published'), ) product_status = models.CharField(choices=PRODUCT_STATUS, max_length=100) product_is_featured = models.BooleanField(default=False) product_is_bestseller = models.BooleanField(default=False) product_is_on_sale = models.BooleanField(default=False) high_sell_through = models.BooleanField(default=False) price_regular = models.DecimalField(max_digits=5, decimal_places=2, blank=True, null=True) price_sale = models.DecimalField(max_digits=5, decimal_places=2, blank=True, null=True) product_UPC = models.CharField(max_length=300, blank=True, null=True) product_SKU = models.CharField(max_length=300, blank=True, null=True) product_MODEL_ID = models.CharField(max_length=300, blank=True, null=True) product_stock_management = models.ForeignKey(ProductStockManagement, on_delete=models.CASCADE, related_name='product_stock_management') product_attributes_set = models.ForeignKey(ProductAttributesSets, on_delete=models.CASCADE, related_name='product_attributes_set') product_specifications_set = models.ForeignKey(ProductSpecificationsSets, on_delete=models.CASCADE, related_name='product_specifications_set') product_details_list = models.ForeignKey(ProductDetailsList, on_delete=models.CASCADE, related_name='product_details_list') product_variations = models.ManyToManyField(ProductVariations, blank=True, max_length=1000) category = models.ForeignKey(Category, on_delete=models.CASCADE) brand = models.ForeignKey(Brand, on_delete=models.CASCADE, related_name='brand') vendor = models.ForeignKey('vendors.Vendor', on_delete=models.CASCADE) #---------------------------------------------------------------- featured_image = models.ImageField(upload_to='media/PRODUCT/FEATURED_IMAGES/', null=True, blank=True) image_galley = models.ForeignKey(ImageGallery, on_delete=models.CASCADE, related_name='product_image_gallery') #---------------------------------------------------------------- title = models.CharField(max_length=1000) slug = models.SlugField(max_length=1000) description_short = RichTextField() description_full = RichTextField() date_created = models.DateTimeField(auto_now_add=True) date_updated = models.DateTimeField() def get_absolute_url(self): return reverse('inventory:product', args=[self.slug]) def __str__(self): return self.title
视图代码
def single_product_add_to_cart(request): cart = Cart(request) if request.POST.get("action") == "post": product_id = int(request.POST.get("productid")) product_qty = int(request.POST.get("productqty")) product = get_object_or_404(Product, id=product_id) cart.add(product=product, qty=product_qty) cartqty = cart.__len__() response = JsonResponse({'qty': cartqty}) return response
购物车页面cart.html
<h2>Cart details:</h2> <hr> {% if cart %} {% for item in cart %} {% with product=item.product %} {{product.id}} {{product.title}} {{item.qty}} {{item.price}}<br> {% endwith %} {% endfor %} {% endif %} <div class="coupon_section"> <form> <input type="text" id="coupon_code" class="coupon_code" name="coupon_code"> <button class="apply_coupon" id="apply_coupon" name="apply_coupon">apply coupon</button> </form> </div> </div>
购物车模型代码
class CartItems(models.Model): product = models.ForeignKey(Product, on_delete=models.CASCADE) quantity = models.CharField(max_length=200) def __str__(self): return str(self.product) class Cart(models.Model): user = models.ForeignKey(CustomUser, on_delete=models.CASCADE) cart_items = models.ManyToManyField(CartItems) def __str__(self): return str(self.user) def add(self, product): self.cart_items = self.cart_items
Cart.py代码
class Cart: def __init__(self, request): self.session = request.session cart = self.session.get(settings.CART_SESSION_ID) if settings.CART_SESSION_ID not in request.session: cart = self.session[settings.CART_SESSION_ID]={} self.cart = cart def add(self, product, qty): product_id = str(product.id) if product_id in self.cart: self.cart[product_id]["qty"] = qty else: self.cart[product_id] = {"price":str(product.price_regular), "qty":qty} self.save() def update(self, product, qty): product_id = str(product) if product_id in self.basket: self.basket[product_id]["qty"] = qty self.save() def delete(): pass def __len__(self): return sum(item["qty"] for item in self.cart.values()) def __iter__(self): product_ids = self.cart.keys() products = Product.objects.filter(id__in=product_ids) cart = self.cart.copy() for product in products: cart[str(product.id)]["product"] = product for item in cart.values(): item["price"] = Decimal(item["price"]) item["total_price"] = item["price"] * item["qty"] yield item def get_subtotal(self): return sum(Decimal(item["price"]) * item["qty"] for item in self.cart.values()) def get_grand_total(self): newprice = 0.00 subtotal = sum(Decimal(item["price"]) * item["qty"] for item in self.cart.values()) if "purchase" in self.session: pass total = subtotal + Decimal(newprice) return total def clear(): del self.session[settings.CART_SESSION_ID] del self.session["address"] del self.session["purchase"] self.save() def save(self): self.session.modified = True
问题原因及解决办法
核心问题
你的Cart类的__iter__方法里,虽然通过Product.objects.filter(id__in=product_ids)获取了商品对象,但如果商品的price_regular字段为空,在add方法里存入session的price值会是None,后续转Decimal时会报错,导致迭代器无法正常返回包含product对象的item,最终模板里{{product.title}}无法渲染。
另外,数据库模型里的Cart和CartItems并没有被实际使用——当前购物车是基于session的Cart类实现的,数据库模型只是空架子,这个不影响当前问题,但后续如果要持久化购物车需要注意。
修复步骤
处理
price_regular为空的情况
修改Cart类的add方法,确保存入session的价格不为空,比如优先使用促销价,无则用原价,再无则设默认值:def add(self, product, qty): product_id = str(product.id) # 优先取促销价,其次原价,最后默认0.00 product_price = product.price_sale or product.price_regular or Decimal('0.00') if product_id in self.cart: self.cart[product_id]["qty"] = qty else: self.cart[product_id] = {"price": str(product_price), "qty": qty} self.save()添加异常捕获避免迭代中断
在__iter__方法里加入异常处理,防止单个商品的价格问题导致整个购物车迭代失败:from decimal import InvalidOperation def __iter__(self): product_ids = self.cart.keys() products = Product.objects.filter(id__in=product_ids) cart = self.cart.copy() for product in products: cart[str(product.id)]["product"] = product for item in cart.values(): try: item["price"] = Decimal(item["price"]) item["total_price"] = item["price"] * item["qty"] yield item except (InvalidOperation, TypeError): # 价格异常时设默认值,继续返回商品信息 item["price"] = Decimal('0.00') item["total_price"] = Decimal('0.00') yield item验证商品数据
登录后台检查对应商品的price_regular字段是否有值,为空则补充完整。
额外提示
- 当前数据库中的
Cart和CartItems模型未被使用,若需实现登录后购物车同步,需将session购物车的数据同步到数据库模型中。 - Ajax成功回调里可以添加购物车区域的刷新逻辑,避免用户手动刷新页面才能看到更新后的购物车内容。
内容的提问来源于stack exchange,提问作者Arnold Schwartzkopf
相关产品推荐
相关产品推荐

