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

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类实现的,数据库模型只是空架子,这个不影响当前问题,但后续如果要持久化购物车需要注意。

修复步骤

  1. 处理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()
    
  2. 添加异常捕获避免迭代中断
    在__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
    
  3. 验证商品数据
    登录后台检查对应商品的price_regular字段是否有值,为空则补充完整。

额外提示

  • 当前数据库中的Cart和CartItems模型未被使用,若需实现登录后购物车同步,需将session购物车的数据同步到数据库模型中。
  • Ajax成功回调里可以添加购物车区域的刷新逻辑,避免用户手动刷新页面才能看到更新后的购物车内容。

内容的提问来源于stack exchange,提问作者Arnold Schwartzkopf

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 09:57:04