Как исправить ошибку "'id': выберите правильный выбор" при проверке Modelformset? - PullRequest
2 голосов
/ 05 января 2020

У меня есть 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 %}

Так что я делаю не так?

-------- ОБНОВЛЕНИЕ ------------

Из ответа Олега я изменил свою проверку набора форм.

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()

Ответы [ 3 ]

1 голос
/ 14 января 2020

Проблема возникла из-за разных моделей, используемых в 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
1 голос
/ 08 января 2020

Эта ошибка указывает на неверный выбор в ModelChoiceField, который в приведенном примере имеет значение status поле TaskCheckForm .

Это атрибут уровня класса , который запускается только после запуска приложения и импорта TaskCheckForm в первый раз. И его QuerySet разрешается только один раз при запуске - и он увидит имеющиеся в то время TaskStatus объекты и никогда не обновит свой список выбора для новых или удаленных элементов.

Для обработки полей отношений и другого с помощью динамических элементов. c queryset можно использовать рекомендованный способ - определить 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 к элементам, которые необходимо перебрать с результатом , как в примере ; секунда - commit=False означает, что экземпляры не будут сохранены, что нормально, так как некоторые дополнительные атрибуты устанавливаются позже, но впоследствии не вызывается instance.save() - поэтому все равно никакие изменения не будут сохранены.

0 голосов
/ 12 января 2020

Похоже, ваш запрос не правильный. Вы делаете

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

Это должно быть так -

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

Или вы можете сделать так:

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

Здесь property много на многие поля. Ваш запрос выглядит как поиск по внешнему ключу.

...