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

Django项目中CartAddProductForm的form.is_valid始终返回False求助

解决Django中CartAddProductForm调用is_valid()始终返回False的问题

问题现象

开发个人Django项目时,调用form.is_valid()时,CartAddProductForm始终返回False,提交商品到购物车后,购物车页面为空。打印错误信息无具体内容,输出如下:

form.is_valid = False
request.POST = <QueryDict: {'sizes': ['S'], 'csrfmiddlewaretoken': ['mytoken']}>
form.errors =
form.non_field_errors = <bound method BaseForm.non_field_errors of <CartAddProductForm bound=False, valid=False, fields=(sizes)>>
field_errors = [('Sizes', [])]

代码详情

forms.py

从数据库获取商品尺寸生成ChoiceField:

class CartAddProductForm(ModelForm):
    class Meta:
        model = Product
        fields = ['sizes']

    def __init__(self, pk: int, *args, **kwargs) -> None:
        super(CartAddProductForm, self).__init__(*args, **kwargs)
        sizes = tuple(Product.objects.get(pk=pk).sizes)
        sizes_list = []
        for item in sizes:
            sizes_list.append((item, item))
        self.fields['sizes'] = forms.ChoiceField(choices=sizes_list)

views.py

product_detail视图初始化表单,add_to_cart视图处理提交请求:

product_detail视图

def product_detail(request: WSGIRequest, product_id: int, product_slug: str) -> HttpResponse:
    product = get_object_or_404(Product,
                                id=product_id,
                                slug=product_slug,
                                available=True)
    categories = Category.objects.all()
    cart_product_form = CartAddProductForm(instance=product, pk=product_id)
    return render(request, 'shop/product/detail.html', {'product': product,
                                                        'categories': categories,
                                                        'cart_product_form': cart_product_form})

add_to_cart视图

@require_POST
def add_to_cart(request: WSGIRequest, product_id: int) -> HttpResponseRedirect:
    cart = Cart(request)
    product = get_object_or_404(Product, id=product_id)
    form = CartAddProductForm(instance=product, pk=product_id)
    if form.is_valid():
        cd = form.cleaned_data
        print(f'cd = {cd}')
        cart.add(product=product,
                 quantity=cd['quantity'],
                 update_quantity=cd['update'])
    else:
        print(f'form.is_valid = {form.is_valid()}')
        print(f'request.POST = {request.POST}')
        print(f'form.errors = {form.errors}')
        print(f'form.non_field_errors = {form.non_field_errors}')
        field_errors = [(field.label, field.errors) for field in form]
        print(f'field_errors = {field_errors}')
    return redirect('cart:cart_detail')

模板文件

{% extends "shop/base.html" %}
<head>
    <meta charset="UTF-8">
    <title>Detail</title>
</head>
<body>
{% block title %}
    {{ product.name }}
{% endblock %}
{% block content %}
    <br>
    <b>{{ product.name }} </b> <br>
    <i>{{ product.description }} </i> <br>
    {{ product.price }} <br>
    <img src="{{ product.image.url }}" width="300" height="500"> <br>
    Доступные размеры: <br>
    {{ product.sizes }}<br>
    <form action="{% url "cart:add_to_cart" product.id %}" method="post">
        {{ cart_product_form }}
        {{ form.as_p }}
        {% csrf_token %}
        <input type="submit" value="Add to cart">
    </form>
{% endblock %}
</body>

urls.py

urlpatterns = [
    re_path(r'^$', views.cart_detail, name='cart_detail'),
    re_path(r'^add/(?P<product_id>\d+)/$', views.add_to_cart, name='add_to_cart'),
    re_path(r'^remove/(?P<product_id>\d+)/$', views.cart_remove, name='cart_remove'),
    ]

问题根源

  1. 表单未绑定POST数据:add_to_cart视图中初始化CartAddProductForm时,未传入request.POST数据,导致表单处于未绑定状态(bound=False),未绑定表单调用is_valid()会直接返回False。
  2. 冗余的instance参数:该表单用于接收用户选择的尺寸,无需绑定Product实例,传入instance=product属于无效操作,可能干扰验证逻辑。
  3. 模板冗余代码:模板中{{ form.as_p }}对应的变量不存在于上下文,会输出空内容,无实际作用。
  4. 尺寸字段转换潜在问题:若Product模型的sizes是字符串类型(如逗号分隔),直接转tuple会将每个字符拆分为选项,导致选项不匹配提交值。

解决方案

1. 修改add_to_cart视图,绑定POST数据

调整表单初始化代码,传入request.POST并移除冗余的instance参数:

@require_POST
def add_to_cart(request: WSGIRequest, product_id: int) -> HttpResponseRedirect:
    cart = Cart(request)
    product = get_object_or_404(Product, id=product_id)
    # 绑定POST数据,仅传入必要的pk参数
    form = CartAddProductForm(pk=product_id, data=request.POST)
    if form.is_valid():
        cd = form.cleaned_data
        print(f'cd = {cd}')
        # 若表单未定义quantity和update字段,需添加对应字段或调整逻辑
        cart.add(product=product,
                 quantity=cd.get('quantity', 1),
                 update_quantity=cd.get('update', False))
    else:
        print(f'form.is_valid = {form.is_valid()}')
        print(f'request.POST = {request.POST}')
        print(f'form.errors = {form.errors}')
        print(f'form.non_field_errors() = {form.non_field_errors()}')  # 调用方法而非引用
        field_errors = [(field.label, field.errors) for field in form]
        print(f'field_errors = {field_errors}')
    return redirect('cart:cart_detail')

2. 清理模板冗余代码

移除模板中多余的{{ form.as_p }}:

<form action="{% url "cart:add_to_cart" product.id %}" method="post">
    {{ cart_product_form }}
    {% csrf_token %}
    <input type="submit" value="Add to cart">
</form>

3. 修正尺寸字段转换逻辑

如果Product模型的sizes是逗号分隔的字符串,修改forms.py中的转换代码:

def __init__(self, pk: int, *args, **kwargs) -> None:
    super(CartAddProductForm, self).__init__(*args, **kwargs)
    # 处理逗号分隔的尺寸字符串,如"S,M,L"
    sizes_str = Product.objects.get(pk=pk).sizes
    sizes = sizes_str.split(',')
    sizes_list = [(item.strip(), item.strip()) for item in sizes]
    self.fields['sizes'] = forms.ChoiceField(choices=sizes_list)

4. 调整product_detail视图的表单初始化

移除冗余的instance参数:

cart_product_form = CartAddProductForm(pk=product_id)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 10:05:32