【问题标题】:How to fix the error " 'id': Select a valid choice" on Modelformset validation?如何修复 Modelformset 验证中的错误“'id': Select a valid choice”?
【发布时间】:2020-01-05 13:12:23
【问题描述】:

我有一个在提交时引发此验证错误的 Modelformset:

{'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)

我已经知道问题与 'status' 字段无关。实际上我认为这与 'task' 字段有关。正如我在其他一些问题上看到的那样,我还将{{ 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()

【问题讨论】:

  • 我只是在这里猜测,但您可以尝试从分配给您的状态字段的小部件的 to_attrs 中删除 "id": "type" 吗?
  • @ruhaib "id":"type" 是在我的表单上引用 CSS id。我试过了,没有区别..谢谢!
  • 这可能是因为您的 TaskCheckForm 和模型 task.ForeignKey 中缺少“task”字段。ForeignKey 没有“null=True”。您可以尝试将“任务”添加到您的 TaskCheckForm 字段吗?
  • 这是 django.forms.models 或 django.forms.fields 中非常普遍的默认错误消息。可能有必要编写该异常的回溯以更具体。

标签: python django django-models django-forms


【解决方案1】:

由于 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

【讨论】:

    【解决方案2】:

    此错误指向 ModelChoiceField 中的无效选择,在提供的示例中是 TaskCheckFormstatus 字段/strong>.

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

    使用动态查询集 recommended way can be used 处理关系字段和其他 - 在字段上定义 empty 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 属性结果like in example;第二 - commit=False 表示不会保存实例,这没关系,因为稍后会设置一些附加属性,但之后不会调用 instance.save() - 所以仍然不会保存任何更改。

    【讨论】:

    • 感谢 Oleg,我在表单中添加了 __init__ 函数,但错误仍然存​​在。我再次尝试的是从任何地方删除状态字段,它仍然给出同样的错误。这就是为什么我认为问题与状态无关,反正我会像你说的那样使用。 tasks = Task.objects.filter(property=property_pk)与qs查询集相同,我可能会删除它。
    • 另外,为什么 form 和 formset 是为TaskCheck 而queryset 是为Task
    • 那是因为我的依赖。 TaskCheck 的目的是保存特定 Task 的状态,一个 Task 可以关联特定的属性。所以我的查询集通过实例属性的任务。我将更新我的问题并插入任务模型以便更好地理解。
    • formset = TaskCheckFormset(request.POST, request.FILES, queryset=qs) - formset 使用 TaskCheck 模型,但 qs 用于 Task 模型。这可能会导致不兼容的字段等。
    • 我将尝试创建一个返回相同结果的 TaskCheck 查询。
    【解决方案3】:

    您的查询似乎不正确。你在做

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

    应该是这样的-

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

    或者你可以这样做:

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

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

    【讨论】:

    • 感谢您的帮助,不幸的是我的问题与此无关。表单集无效,因此它甚至没有达到这一点。不管怎样,我做了这些改变。
    猜你喜欢
    • 2022-01-06
    • 1970-01-01
    • 2011-08-29
    • 2012-06-10
    • 1970-01-01
    • 1970-01-01
    • 2019-08-03
    • 1970-01-01
    • 2019-09-07
    相关资源
    最近更新 更多