如何修复模型表单集验证时的错误" 'id': Select a valid choice"?



我有一个模型集,它在提交时引发此验证错误:

{'id': ['选择一个有效的选项。这种选择不是 可用选项。

此错误出现的次数与查询集中的对象相同,即:

qs = Task.objects.filter(property=property)

在过去的几天里,我一直在尝试解决这个问题。我读过很多其他类似的帖子并尝试了不同的解决方案,但没有一个对我有用。

我的表单集可以在这里看到:

def add_taskcheck(request, property_pk, pk):
property = get_object_or_404(Property, pk=property_pk)
pcheck = get_object_or_404(Propertycheck, pk=pk)
qs = Task.objects.filter(property=property)
tasks = Task.objects.filter(property=property_pk)
TaskCheckFormset = modelformset_factory(TaskCheck, form=TaskCheckForm, fields=('status','image','notes'), extra=0)
if request.method == 'POST':
formset = TaskCheckFormset(request.POST, request.FILES, queryset=qs)
print(formset.errors)
if formset.is_valid():
taskcheck = formset.save(commit=False)
taskcheck.property_check=pcheck.id
return HttpResponseRedirect(reverse('propertycheck:details', args=[pk]))
else:
formset = TaskCheckFormset(queryset=qs)
context = {
'title':"Add Property Check",
'task':tasks,
'reference':property_pk,
'formset':formset,
}
return render(request, 'propertycheck/add-taskcheck.html', context)

和我的表格:

class TaskCheckForm(forms.ModelForm):
status = forms.ModelChoiceField(queryset=TaskStatus.objects.all(), to_field_name="name", widget=forms.Select(attrs={
'class':'form-control custom-select',
'id':'type',
}))
image = ...
notes = ...
class Meta:
model = TaskCheck
fields = ('status','image','notes')

最后是我的模型:

class TaskCheck(models.Model):
status = models.ForeignKey(TaskStatus)
image = models.ImageField(upload_to='task_check', blank=True, null=True)
notes = models.TextField(max_length=500, blank=True)
task = models.ForeignKey(Task)
property_check = models.ForeignKey(Propertycheck)
class Task(models.Model):
task = models.CharField(max_length=100, unique=True)
category = models.ForeignKey(Categories)
property = models.ManyToManyField(Property)

我已经知道问题与"状态">字段无关。实际上,我认为这与"任务">字段有关。正如我在其他一些问题上看到的那样,我还{{ form.id }}添加到模板中。

作为参考,我的模板:

{% csrf_token %}
{{ formset.management_form }}
{% for form in formset %}
{{ form.id }}
<div class="form-group">
<h5 class="card-title">{{ form.instance.task }}</h5>
<div class="row">
<div class="col-md-3">
<label for="pname">Status</label>
{{ form.status }}
{{ form.status.errors }}
</div>
<div class="col-md-3">
<label for="pname">Image</label>
{{ form.image }}
{{ form.image.errors }}
</div>
<div class="col-md-3">
<label for="pname">Notes</label>
{{ form.notes }}
{{ form.notes.errors }}
</div>
</div>
</div>
{% endfor %}

那我做错了什么呢?

--------更新------------

从 Oleg 的回答中,我更改了我的表单集验证。

if request.method == 'POST':
formset = TaskCheckFormset(request.POST, request.FILES, queryset=qs)
if formset.is_valid():
instances = formset.save(commit=False)
for instance in instances:
# do something with instance
instance.property_check=pcheck.id
instance.save()

由于ModelFormSet和QuerySet中使用的不同模型而引发的问题。 这些问题可以通过使用以下方法解决:

property = get_object_or_404(Property, pk=property_pk)
pcheck = get_object_or_404(Propertycheck, pk=pk)
qs = Task.objects.filter(property=property)
category = qs.values('category').distinct()
TaskCheckFormset = formset_factory(TaskCheckForm,extra=len(qs))
formset = TaskCheckFormset()
for i in range(len(qs)):
formset.forms[i].initial['task']=qs[i].id
formset.forms[i].instance.task=qs[i]
formset.forms[i].instance.property_check=pcheck
formset.forms[i].initial['property_check']=pcheck.id

此错误指向ModelChoiceField中的无效选择,在提供的示例中是TaskCheckForm的状态字段

这是类级别的属性,仅在应用程序启动且首次导入TaskCheckForm时启动。 它的QuerySet在开始时只解析一次 - 它将看到当时TaskStatus对象,并且永远不会更新其新项目或删除项目的选择列表。

要处理关系字段和其他具有动态查询集的推荐方法,可以使用 - 在字段上定义的 QuerySet 并将其设置为表单__init__方法中的必需字段:

status = forms.ModelChoiceField(queryset=TaskStatus.objects.none(), to_field_name="name", widget=...)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['status'].queryset = TaskStatus.objects.all()

代码中的其他潜在问题位置:

  • tasks = Task.objects.filter(property=property_pk)- 将返回结果列表。但是稍后在代码中被分配给模板中的task变量,该变量可能期望(但可能是可以的,并且它期望列表(单个项目。您可以改用tasks = Task.objects.filter(property=property_pk).first()

  • taskcheck = formset.save(commit=False)- 首先,它返回一个项目列表(因为它formset对一组表单进行操作(,因此为了将property_check属性添加到项目,您需要迭代结果,例如; 第二 -commit=False表示实例不会被保存,这没关系,因为稍后会设置一些额外的属性, 但之后不会调用任何instance.save()- 因此仍然不会保存任何更改。

看起来您的查询不正确。你正在做

qs = Task.objects.filter(property=property)

应该是这样的——

qs = Task.objects.filter(property__id=property.id)

或者你可以这样做:

qs = Task.objects.filter(property__in=[property_pk])

这里property是一个多对多的领域。您的查询看起来像搜索外键。

最新更新