Django中QueryDict数据重复致表单验证失败问题排查
问题现象
在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:移除冗余字段(推荐)
- 删除模板中手动添加的hidden输入框:
{% if task.id %} <input type="hidden" name="task" value="{{ task.id }}"> {% endif %}
- 修改
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
相关产品推荐
相关产品推荐

