[Django]-How to fix the error " 'id': Select a valid choice" on Modelformset validation?

2👍

The issue raised because of different models used in ModelFormSet and QuerySet.
The issues can be solved by using:

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👍

This error points to invalid choice in ModelChoiceField that in provided example is status field of TaskCheckForm.

This is class level attribute and is initiated only once application starts and TaskCheckForm is being imported for the first time.
And its QuerySet is resolved only once at start – and it will see present at that time TaskStatus objects and never update its choices list for new or deleted items.

To handle relationship fields and other with dynamic queryset recommended way can be used – define empty QuerySet on field and set it to required one in form’s __init__ method:

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

Other potential problem places in code:

  • tasks = Task.objects.filter(property=property_pk) – will return a list of results. But later in code is assigned to task variable in template which may expect (but may be it is ok and it expects list) single item. You can use tasks = Task.objects.filter(property=property_pk).first() instead.

  • taskcheck = formset.save(commit=False) – first, it returns a list of items (because it is formset to act on a set of forms), so in order to add property_check attribute to items you need to iterate over result like in example; second – commit=False means instances will not be saved, which is ok as some additional attribute is set later, but no instance.save() is called afterwards – so still no changes will be saved.

0👍

Looks like your query is not correct. You are doing

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

It should be like this-

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

Or you can do like this:

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

Here property is a many to many field. Your query looks like searching foreign key.

Leave a comment