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

Django实现勾选Checkbox添加带选中尺码的商品至购物车

问题描述

目前已实现商品添加至购物车功能,但需优化为:用户通过Checkbox/Radio选择商品尺码后,点击“加入购物车”按钮,将商品与选中尺码一同添加至购物车。现有代码无法实现该逻辑,需调整。


现有代码展示

商品、尺码及购物车模型

class Product(models.Model):
    article = models.CharField(max_length=12,null=False,blank=False,unique=True,primary_key=True)
    img = models.ImageField(upload_to='products_images')
    price = models.DecimalField(max_digits=9,decimal_places=2)
    description = models.TextField(null=True,blank=True)
    gender = models.CharField(max_length=12,null=False,blank=False)
    is_child = models.BooleanField(null=False)
    season = models.CharField(max_length=12,null=False)
    color = models.CharField(max_length=24,null=False)
    sizes = models.ForeignKey(to='Size',on_delete=models.CASCADE,to_field='article')
 
    def __str__(self):
        return f"{self.article}"
 
class Size(models.Model):
    article = models.CharField(max_length=12,null=False,unique=True)
    article_size = models.ForeignKey(to='ArticleSize',on_delete=models.CASCADE,to_field='article_size')
 
    def __str__(self):
        return f"{self.article}"
 
class ArticleSize(models.Model):
    article_size = models.CharField(max_length=12, null=False, unique=True)
    size_name = models.CharField(max_length=12,null=False,blank=False)
    qty = models.SmallIntegerField()
    to_article = models.CharField(max_length=12, null=False)
 
    def __str__(self):
        return f"{self.article_size}"
 
class Basket(models.Model):
    user = models.ForeignKey(to=User, on_delete=models.CASCADE)
    product = models.ForeignKey(to=Product, on_delete=models.CASCADE,to_field='article')
    size = models.ForeignKey(to=ArticleSize,on_delete=models.CASCADE,to_field='article_size')
    qty = models.PositiveSmallIntegerField(default=0)
    create_time_stamp = models.DateTimeField(auto_now_add=True)
    def __str__(self):
        return f"Корзина для {self.user.email} товар {self.product.article},размер {self.size.size_name}"

表单模型

class CheckboxForm(forms.Form):
    checkbox_size = forms.CharField(widget=forms.CheckboxInput(attrs={
    'class':'size_checkbox', 'type':'checkbox', 'name':'size_checkbox'}),required=True)
 
    class Meta:
        model = ArticleSize
        fields = ('checkbox_size')

视图代码

def products(request):
    form = CheckboxForm()
 
    context = {
        'title': "Магазин",
        'footer_1': "127015, Москва, Бумажный пр-д., д. 14, стр. 2 ООО «НИКАМЕД».",
        'footer_2': "Копирование материалов запрещено.",
        'products': Product.objects.all(),
        'sizes': Size.objects.all(),
        'article_size': ArticleSize.objects.all(),
        'form': form,
 
    }
    return render(request, 'products/products.html', context)
 
def basket_add(request,product,size):
    product = Product.objects.get(article=product)
    size = ArticleSize.objects.get(article_size=size)
    basket = Basket.objects.filter(user=request.user,product=product,size=size)
 
    if not basket.exists():
        Basket.objects.create(user=request.user,product=product,qty=1,size=size)
    else:
        basket = basket.last()
        basket.qty += 1
        basket.save()
    return HttpResponseRedirect(request.META['HTTP_REFERER'])

HTML模板

<div class="goods-group">
                   {% for product in products %}
                       <div class="goods">
                               <div class="shoe-photo">
                                   <a href="{% url 'products:item_info' %}">
                                        <img src="{{ product.img.url }}" alt="нет фото">
                                   </a>
                                   <a href="#">
                                        <svg class="photo-heart" fill="#C0BFBF" width="64px" height="64px" viewBox="0 0 200 200" data-name="Layer 1" id="Layer_1" xmlns="http://www.w3.org/2000/svg"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"><title></title><path d="M171.28,41.69a48.29,48.29,0,0,0-68.5,0l-2.5,3-2.5-2.5a48.29,48.29,0,0,0-68.5,0c-19,18.5-19,49-1,68l50,53a29.92,29.92,0,0,0,43.5,0l50.5-53.5c17.5-19.5,17-49.5-1-68Zm-14,53.5-50.5,53.5a10.26,10.26,0,0,1-14.5,0l-50-53c-10.5-11.5-10.5-29,.5-40s29-11,40.5,0l2.5,2.5c8,8,20.5,8,28,0l2.5-2.5a28.37,28.37,0,0,1,40,0,27.78,27.78,0,0,1,1,39.5Z"></path></g></svg>
                                   </a>
                                </div>
 
                           <a href="#">
                                <div class="price">{{product.price}} руб</div>
                           </a>
                           <a href="#">
                                <div class="description">{{product.description}}</div>
 
                           </a>
 
                           <div class="sizes-group">
                                   <form action="" method="post">
                                       {% for item in sizes %}
                                           {% if item.article == product.article %}
                                                {% for size in article_size %}
                                                    {% if size.to_article == item.article %}
                                                           <label>
                                                               {{form.checkbox_size}}
                                                               <span class="title_checkbox">{{size.size_name}}</span>
                                                           </label>
                                                    {% else %}
                                                    {% endif %}
                                                {% endfor %}
                                           {% else %}
                                           {% endif %}
                                       {% endfor %}
                                   </form>
                           </div>
 
                           <a href="{% url 'products:basket_add' product=product.article size=product.sizes.article_size %}">
                               <button class="btn" type="submit">
                                   <svg class="button-cart" fill="#C0BFBF" width="64px" height="64px" viewBox="-2 -2 204.00 204.00" data-name="Layer 1" id="Layer_1" xmlns="http://www.w3.org/2000/svg"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"><title></title><path d="M183.25,52.75a18.22,18.22,0,0,0-7-1h-91c-3.5,0-8-.5-11.5.5a9.64,9.64,0,0,0-7.5,9.5,10,10,0,0,0,10,10h90.5l-8,42a10.22,10.22,0,0,1-10,8h-75a10.66,10.66,0,0,1-10-8l-16-74a20.3,20.3,0,0,0-19.5-16h-7a10,10,0,0,0,0,20h7l16,74c3,14,15,23.5,29.5,23.5h75c12,0,24.5-8,28-19.5,2.5-8,4-17,5.5-25,1.5-8.5,3.5-17,5-25a17.76,17.76,0,0,0,1-5,17,17,0,0,0,.5-5c0-4-2-8-5.5-9Zm-132,108.5a15,15,0,0,0,30,0h0a15,15,0,0,0-30,0Zm90,0a15,15,0,1,0,15-15,15,15,0,0,0-15,15v0Z"></path></g></svg>
                                   В корзину</button>
                           </a>
                       </div>
                   {% endfor %}
               </div>
           </div>

解决方案

一、优化模型结构(推荐)

原模型存在冗余的Size中间表,直接关联Product与ArticleSize即可,简化查询逻辑:

class Product(models.Model):
    article = models.CharField(max_length=12,null=False,blank=False,unique=True,primary_key=True)
    img = models.ImageField(upload_to='products_images')
    price = models.DecimalField(max_digits=9,decimal_places=2)
    description = models.TextField(null=True,blank=True)
    gender = models.CharField(max_length=12,null=False,blank=False)
    is_child = models.BooleanField(null=False)
    season = models.CharField(max_length=12,null=False)
    color = models.CharField(max_length=24,null=False)

    def __str__(self):
        return f"{self.article}"

class ArticleSize(models.Model):
    article_size = models.CharField(max_length=12,</think_never_used_51bce0c785ca2f68081bfa7d91973934>
我继续完成优化后的模型和后续步骤:

```python
class ArticleSize(models.Model):
    article_size = models.CharField(max_length=12, null=False, unique=True)
    size_name = models.CharField(max_length=12,null=False,blank=False)
    qty = models.SmallIntegerField()
    # 直接关联Product,替代原有的to_article字段
    product = models.ForeignKey(to=Product, on_delete=models.CASCADE, related_name='sizes')

    def __str__(self):
        return f"{self.size_name} ({self.product.article})"

# Basket模型保持不变
class Basket(models.Model):
    user = models.ForeignKey(to=User, on_delete=models.CASCADE)
    product = models.ForeignKey(to=Product, on_delete=models.CASCADE,to_field='article')
    size = models.ForeignKey(to=ArticleSize,on_delete=models.CASCADE,to_field='article_size')
    qty = models.PositiveSmallIntegerField(default=0)
    create_time_stamp = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return f"Корзина для {self.user.email} товар {self.product.article},размер {self.size.size_name}"

二、修改表单类

针对每个商品动态生成尺码选择表单,使用单选框(用户通常仅选一个尺码):

class AddToBasketForm(forms.Form):
    size = forms.ModelChoiceField(
        queryset=ArticleSize.objects.none(),
        widget=forms.RadioSelect,
        required=True,
        label='Выберите размер'
    )

    def __init__(self, *args, **kwargs):
        # 接收商品ID,动态过滤尺码选项
        product_article = kwargs.pop('product_article')
        super().__init__(*args, **kwargs)
        self.fields['size'].queryset = ArticleSize.objects.filter(product__article=product_article)

三、调整视图逻辑

  1. 商品列表视图:为每个商品绑定对应的尺码表单
def products(request):
    products_with_forms = []
    for product in Product.objects.all():
        form = AddToBasketForm(product_article=product.article)
        products_with_forms.append({'product': product, 'form': form})

    context = {
        'title': "Магазин",
        'footer_1': "127015, Москва, Бумажный пр-д., д. 14, стр. 2 ООО «НИКАМЕД».",
        'footer_2': "Копирование материалов запрещено.",
        'products_with_forms': products_with_forms,
    }
    return render(request, 'products/products.html', context)
  1. 购物车添加视图:处理POST请求获取选中的尺码
from django.shortcuts import get_object_or_404

def basket_add(request):
    if request.method == 'POST':
        product_article = request.POST.get('product_article')
        form = AddToBasketForm(request.POST, product_article=product_article)
        if form.is_valid():
            product = get_object_or_404(Product, article=product_article)
            size = form.cleaned_data['size']
            # 用get_or_create简化重复商品的数量累加逻辑
            basket, created = Basket.objects.get_or_create(
                user=request.user,
                product=product,
                size=size,
                defaults={'qty': 1}
            )
            if not created:
                basket.qty += 1
                basket.save()
    return HttpResponseRedirect(request.META['HTTP_REFERER'])

四、更新HTML模板

将表单与提交按钮整合,确保选中的尺码能随POST请求传递:

<div class="goods-group">
    {% for item in products_with_forms %}
        {% with product=item.product form=item.form %}
        <div class="goods">
            <div class="shoe-photo">
                <a href="{% url 'products:item_info' %}">
                    <img src="{{ product.img.url }}" alt="нет фото">
                </a>
                <a href="#">
                    <svg class="photo-heart" fill="#C0BFBF" width="64px" height="64px" viewBox="0 0 200 200" data-name="Layer 1" id="Layer_1" xmlns="http://www.w3.org/2000/svg">
                        <g id="SVGRepo_bgCarrier" stroke-width="0"></g>
                        <g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g>
                        <g id="SVGRepo_iconCarrier">
                            <title></title>
                            <path d="M171.28,41.69a48.29,48.29,0,0,0-68.5,0l-2.5,3-2.5-2.5a48.29,48.29,0,0,0-68.5,0c-19,18.5-19,49-1,68l50,53a29.92,29.92,0,0,0,43.5,0l50.5-53.5c17.5-19.5,17-49.5-1-68Zm-14,53.5-50.5,53.5a10.26,10.26,0,0,1-14.5,0l-50-53c-10.5-11.5-10.5-29,.5-40s29-11,40.5,0l2.5,2.5c8,8,20.5,8,28,0l2.5-2.5a28.37,28.37,0,0,1,40,0,27.78,27.78,0,0,1,1,39.5Z"></path>
                        </g>
                    </svg>
                </a>
            </div>

            <a href="#">
                <div class="price">{{product.price}} руб</div>
            </a>
            <a href="#">
                <div class="description">{{product.description}}</div>
            </a>

            <div class="sizes-group">
                <form action="{% url 'products:basket_add' %}" method="post">
                    {% csrf_token %}
                    <!-- 隐藏字段传递商品ID -->
                    <input type="hidden" name="product_article" value="{{ product.article }}">
                    <!-- 渲染尺码选择框
相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 00:04:06