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

Django中QueryDict数据重复致表单验证失败问题排查

Django表单提交时hidden字段重复导致验证失败问题排查

问题现象

在Django中创建关联模型、表单、视图及对应HTML页面后,保存数据时发现task字段数据重复。提交表单后浏览器提示"Error UpdateURL Occurred. Please try again.",终端输出显示QueryDict里的task字段存在两个值['4', '']。


相关代码及输出

forms.py

class TaskContentForm(forms.ModelForm):

    task = forms.ModelChoiceField(
        queryset=Task.objects.all(),
        label="Task",
        widget=forms.HiddenInput(),
        required=True,
    )
    position_label = forms.CharField(
        label="Position Label",
        widget=forms.TextInput(attrs={"class": "form-control task_content"}),
        required=True,
    )
    position_name = forms.CharField(
        label="Position Name",
        widget=forms.TextInput(attrs={"class": "form-control task"}),
        required=True,
    )

models.py

class TaskContent(models.Model):
    task = models.ForeignKey(Task, on_delete=models.CASCADE)
    position_label = models.CharField(max_length=100)
    position_name = models.CharField(max_length=100)

edit_content.html

<div class="row item-row">
  <div class="col-12">
    <div class="card card-body bg-white border-light shadow-sm mb-4">
      <h2 class="h5 mb-4">Transaction information</h2>
      <form action="{% url 'task_contents' task=task.id pk=task_content.id %}" method="post"> {% csrf_token %} {% if task.id %} <input type="hidden" name="task" value="{{ task.id }}"> {% endif %} <div class="row"> {% for field in form %} <div class="col-md-6 mb-3">
            <div>{{ field.label_tag }}{{ field }}</div>
          </div> {% endfor %} </div>
        <div class="mt-3">
          <a class="btn btn-danger delete_item" data-href="{% url 'task_contents' task=task.id pk=task_content.id action='single' %}">Delete</a>
          <button type="submit" class="btn btn-primary">Save</button>
        </div>
      </form>
    </div>
  </div>
</div>

views.py(TaskContentView类的update_instance方法)

def update_instance(self, request, task, pk, is_urlencode=False):
    task_content = self.get_object(pk)

    print("Before form submission:")
    print(task_content.__dict__)

    task_obj = get_object_or_404(Task, id=task)
    form_data = QueryDict(request.body) if is_urlencode else request.POST

    print("Task:", task)
    print("Form data:", form_data)

    form = TaskContentForm(form_data, instance=task_content)
    if form.is_valid():
        task_content_instance = form.save(commit=False)
        task_content_instance.task = task_obj
        task_content_instance.save()

        if not is_urlencode:
            messages.success(request, "Transaction saved successfully")
        return True, "Transaction saved successfully", form

    if not is_urlencode:
        messages.warning(
            request, "Error UpdateURL Occurred. Please try again."
        )
        print(
            form.errors
        )  # Add this line to display form errors in the console
    return False, "Error Update Occurred. Please try again.", form

终端输出

{'_state': <django.db.models.base.ModelState object at 0x00000203DFEC5A20>, 'id': 8, 'task_id': 4, 'position_label': '1032', 'position_name': 'Kvaka', 'material': 'Silumin', 'material_supplier': '', 'request_date': datetime.date(2023, 5, 6), 'realization_deadline': None, 'construction_tool': '', 'tool_request_date': None, 'tool_realization_deadline': None, 'tool_creation': '', 'tool_creation_request_date': None, 'price': None}
Task: 4
Form data: <QueryDict: {'csrfmiddlewaretoken': ['tZ4zbHs5HeRhQHB1T4vuo66DwK5FMl7gcHyFjy3EzegHE4DsNjktlLnW9DQkDX1T'], 'task': ['4', ''], 'position_label': ['1032'], 'position_name': ['Kvaka'], 'material': ['Silumin'], 'material_supplier': [''], 'request_date': ['2023-05-06'], 'realization_deadline': [''], 'construction_tool': [''], 'tool_request_date': [''], 'tool_realization_deadline': [''], 'tool_creation': [''], 'tool_creation_request_date': [''], 'price': ['']}>

问题原因

模板edit_content.html中手动添加了一个name="task"的hidden输入框,同时TaskContentForm里也定义了task字段并使用HiddenInput组件,导致页面生成两个同名输入控件:手动添加的控件值为4,表单渲染的控件未被赋值(值为空字符串)。提交后QueryDict中task字段出现两个值,触发表单验证失败。

另外,视图已通过URL参数获取task值,且在保存时手动设置task_content_instance.task = task_obj,完全不需要在表单或模板中传递task字段。


解决方案

方案1:移除冗余字段(推荐)

  1. 删除模板中手动添加的hidden输入框:
{% if task.id %} <input type="hidden" name="task" value="{{ task.id }}"> {% endif %}
  1. 修改TaskContentForm,移除task字段定义,只保留用户需要输入的字段:
class TaskContentForm(forms.ModelForm):
    class Meta:
        model = TaskContent
        fields = ['position_label', 'position_name']
        widgets = {
            'position_label': forms.TextInput(attrs={"class": "form-control task_content"}),
            'position_name': forms.TextInput(attrs={"class": "form-control task"}),
        }

视图中手动设置task的逻辑保留即可。

方案2:保留表单字段并正确赋值(不推荐)

若需保留表单中的task字段,需在视图初始化表单时传入初始值:

form = TaskContentForm(initial={'task': task_obj}, instance=task_content)

同时删除模板中手动添加的hidden输入框,确保表单渲染的task字段带有正确值。


额外优化建议

  • 视图中form_data = QueryDict(request.body) if is_urlencode else request.POST的写法需注意,解析request.body可能存在编码问题,建议使用request.POST.copy()或Django标准方式处理。
  • 利用已添加的print(form.errors)语句查看具体验证错误,可更快定位问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 08:22:07