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

Django测试中Client.post提交表单提示字段必填问题排查

Django测试中Client POST表单无效,但浏览器提交正常的问题

问题详情

  • 测试视图表单逻辑时,Client().post提交的表单在视图中验证失败,但浏览器正常提交功能正常
  • form.errors提示name和address字段必填,但form.data中明明有对应值
  • 测试代码里提交前form.is_valid()返回True,到了视图里却验证不通过

测试代码

@pytest.mark.django_db
def test_add_bar_with_logged_in_user_form(setUpUser, django_user_model):

    from ..models.forms import BarForm
    from django.test import Client

    bar_name = "foo bar2"
    address = "2344 foo st"

    user = setUpUser 

    client = Client()
    client.force_login(user=user)

    client.login(username=user.username, password=user.password, email=user.email)

    form = BarForm()

    form_data = {
        "name": bar_name,
        "address": address,
    }

    form = BarForm(data=form_data)

    assert form.is_valid() is True

    response = client.post(urls.reverse("add_bar"), form.data, content_type="application/x-www-form-urlencoded", follow=True)

    assert response.status_code == 200

视图相关代码

def add_bar(request):

# If this is a POST then the user has submitted the form to add a new bar
if request.method == "POST":

    active_user = auth.get_user(request)

    # Only logged in users should be able to submit
    if not active_user.is_authenticated:
        messages.error(request, "Must be logged in to do that")
        return HttpResponseForbidden("Authentication required")

    form = BarForm(request.POST)

    logger.info(f"*=-===-= form.data : {form.data}")

    if form.is_valid():
        logger.info(f"*=-===-= form.data : {form.data}")
        cleaned_data = form.cleaned_data

        created_by_id = active_user.id

        bar_new = Bar.objects.create(**cleaned_data, submitted_by=active_user)

        messages.success(
            request,
            f" - Thanks! Bar submited.",
        )
        return redirect("index")
    
    elif form.errors:

        errors = form.errors
        messages.error(request, "Form is invalid")

        logger.info(f"** test_add_bar_with_logged_in_user -> form.errors: {errors}")
        for error in form.errors:
            logger.info(f"** test_add_bar_with_logged_in_user -> error: {error}")
        logger.info(f"*=-===-= form.data : {form.data}")
        return redirect("index")
    elif form.non_field_errors():
        for error in form.non_field_errors():
            logger.info(f"** test_add_bar_with_logged_in_user -> error: {error}")
        return False

问题原因及解决方法

问题出在你用form.data作为POST数据传给Client。Django表单的data是QueryDict对象,直接传递它会因为QueryDict的特性(比如不可变性、内部格式),导致视图接收到的数据无法被表单正确解析,看似有值但实际未被识别。

修复步骤:

  1. 把POST请求里的form.data换成原始的form_data字典:
response = client.post(urls.reverse("add_bar"), form_data, content_type="application/x-www-form-urlencoded", follow=True)
  1. 删掉测试中多余的client.login(...)调用——client.force_login(user=user)已经完成了登录,重复调用没必要。

为什么这样能解决?
测试里实例化表单验证通过,说明表单逻辑本身没问题。但form.data是表单处理后的QueryDict,和原始字典的传递格式有差异,Client提交时用原始字典才能保证视图里的request.POST和浏览器提交的格式一致,表单就能正确读取字段值完成验证。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 20:52:15