# Django Model Form Different Choices

**URL:** <https://forum.djangoproject.com/t/django-model-form-different-choices/12493>\
**Category:** Forms & APIs\
**Created:** [March 7, 2022, 8:15pm UTC](https://forum.djangoproject.com/t/django-model-form-different-choices/12493 "2022-03-07T20:15:47Z")\
**Posts on this page:** 18\
**Page:** 2

<div class="post-metadata">

**Author:** ![TommyQuality](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/tommyquality/32/6302_2.png) [@TommyQuality](https://forum.djangoproject.com/u/TommyQuality)\
**Post date:** [March 19, 2022, 11:46am UTC](https://forum.djangoproject.com/t/django-model-form-different-choices/12493/21 "2022-03-19T11:46:18Z")

</div>

> [@KenWhitesell](#):
>
> (You will need to apply the appropriate selection constraint to the queryset. See [Form fields | Django documentation | Django](https://docs.djangoproject.com/en/4.0/ref/forms/fields/#modelchoicefield))

Hi Ken,

Sorry to keep asking for help, but i am looking at the documentation for the queryset to render the correct choices for the select field.

```auto
# A custom empty label
field1 = forms.ModelChoiceField(queryset=..., empty_label="(Nothing)")

```

`queryset=` what is it expecting here? Am i writing the actual query here? Or is it expecting the name of a query created within the view?

I’m probably misunderstanding the documentation, but i can’t see anywhere telling me what it needs to be?

---

<div class="post-metadata">

**Author:** ![KenWhitesell](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/kenwhitesell/32/280_2.png) [@KenWhitesell](https://forum.djangoproject.com/u/KenWhitesell)\
**Post date:** [March 19, 2022, 11:50am UTC](https://forum.djangoproject.com/t/django-model-form-different-choices/12493/22 "2022-03-19T11:50:07Z")

</div>

It’s anything that resolves to a Queryset.

What is a queryset?

What creates a queryset?

See [Making queries | Django documentation | Django](https://docs.djangoproject.com/en/4.0/topics/db/queries/#retrieving-objects-1)

---

<div class="post-metadata">

**Author:** ![TommyQuality](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/tommyquality/32/6302_2.png) [@TommyQuality](https://forum.djangoproject.com/u/TommyQuality)\
**Post date:** [March 19, 2022, 11:52am UTC](https://forum.djangoproject.com/t/django-model-form-different-choices/12493/23 "2022-03-19T11:52:05Z")

</div>

What i mean is the actual queryset logic goes here, not the results of a query set from the view?

```auto
answer = models.ForeignKey(Choice, on_delete=models.CASCADE,null=True, queryset=Choice.objects.filter(something here))

```

---

<div class="post-metadata">

**Author:** ![KenWhitesell](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/kenwhitesell/32/280_2.png) [@KenWhitesell](https://forum.djangoproject.com/u/KenWhitesell)\
**Post date:** [March 19, 2022, 11:53am UTC](https://forum.djangoproject.com/t/django-model-form-different-choices/12493/24 "2022-03-19T11:53:14Z")

</div>

I don’t understand your question.

You can put anything there that returns a queryset.

---

<div class="post-metadata">

**Author:** ![TommyQuality](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/tommyquality/32/6302_2.png) [@TommyQuality](https://forum.djangoproject.com/u/TommyQuality)\
**Post date:** [March 19, 2022, 11:57am UTC](https://forum.djangoproject.com/t/django-model-form-different-choices/12493/25 "2022-03-19T11:57:05Z")

</div>

Ok.

So in a view i have a queryset `something = something.object.filter(something)`

In my model do i put:

````auto
answer = models.ForeignKey(Choice, on_delete=models.CASCADE,null=True, queryset=Choice.objects.filter(something here))```

````

or

```auto
answer = models.ForeignKey(Choice, on_delete=models.CASCADE,null=True, queryset=something

```

where `something` is the results of the query set from the view?

Or maybe I’m completely misunderstanding.

I think `queryset = ` is the same as `something =` so i think i put the actual logic in the model rather than referencing a queryset logic from a view

---

<div class="post-metadata">

**Author:** ![KenWhitesell](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/kenwhitesell/32/280_2.png) [@KenWhitesell](https://forum.djangoproject.com/u/KenWhitesell)\
**Post date:** [March 19, 2022, 12:02pm UTC](https://forum.djangoproject.com/t/django-model-form-different-choices/12493/26 "2022-03-19T12:02:57Z")

</div>

I wouldn’t say you’re _completely_ misunderstanding. I would expression the observation that it appears you’re not fully comfortable with how data is referenced throughout the system.

You can’t just reference `something` in the form when that `something` is defined in the view. You can’t access an object, created in `function_x`, in `function_y` without somehow passing that object to `function_y`. (It can be done, but you need to explicitly do it.)

But what you put there is entirely up to you. You need to decide what that Queryset needs to be.

However, I will point out that this queryset needs to be “adjusted” for each instance of the form being created, because you want different choices to be made available for each question. Each instance of the form being built will need to be “customized” for the question.

---

<div class="post-metadata">

**Author:** ![TommyQuality](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/tommyquality/32/6302_2.png) [@TommyQuality](https://forum.djangoproject.com/u/TommyQuality)\
**Post date:** [March 19, 2022, 12:11pm UTC](https://forum.djangoproject.com/t/django-model-form-different-choices/12493/27 "2022-03-19T12:11:13Z")

</div>

Sorry, I’m being an idiot. I read it as being the queryset was applied to the `models.py` but its actually in the `forms.py`

And basically, i write my quesyset directly within the field I want the results to be shown.

`'answer': forms.Select(choices = Choice.objects.filter(question_id = 1)` something along these lines?

And then within my template i need my select options to reference the `choices` from my quesyset

---

<div class="post-metadata">

**Author:** ![TommyQuality](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/tommyquality/32/6302_2.png) [@TommyQuality](https://forum.djangoproject.com/u/TommyQuality)\
**Post date:** [March 19, 2022, 7:24pm UTC](https://forum.djangoproject.com/t/django-model-form-different-choices/12493/28 "2022-03-19T19:24:51Z")

</div>

hi Ken, I still can’t get this to work, i think its because i am using modelformsets.

```auto
class AnswerForm(ModelForm):
    class Meta:
        model = ProjectQuestionnaireAnswer
        fields = ('answer','notes')

        widgets = {
            'notes': forms.Textarea(attrs={'class': 'form-control','placeholder': 'Add notes to question here'}),
        }

    def __init__ (self, *args, **kwargs):
         question_id = kwargs.pop('question_id')
         super(). __init__ (*args, **kwargs)
         self.fields['answer'].widget = forms.Select(choices = Choice.objects.filter(question_id = question_id),attrs={'class': 'form-control select2'})

```

but i am getting the following error when rendering the form within the template

```auto
# TypeError at /questions/1/1

cannot unpack non-iterable Choice object

```

Any clues?

---

<div class="post-metadata">

**Author:** ![KenWhitesell](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/kenwhitesell/32/280_2.png) [@KenWhitesell](https://forum.djangoproject.com/u/KenWhitesell)\
**Post date:** [March 19, 2022, 10:52pm UTC](https://forum.djangoproject.com/t/django-model-form-different-choices/12493/29 "2022-03-19T22:52:26Z")

</div>

Going to need a lot more detail from the traceback here, along with the view and other directly-related code.

My initial hunch based only on the information provided here is that you’re not passing the question\_id into the form, which is causing the Choice query to be null. But there are more questions than answers at this point.

---

<div class="post-metadata">

**Author:** ![TommyQuality](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/tommyquality/32/6302_2.png) [@TommyQuality](https://forum.djangoproject.com/u/TommyQuality)\
**Post date:** [March 20, 2022, 9:06am UTC](https://forum.djangoproject.com/t/django-model-form-different-choices/12493/30 "2022-03-20T09:06:41Z")

</div>

So just to explain what i’m trying to do.

I have a questionnaire which is made up of `Questionnaire Model, Answer Model, Question Model, Respose Model `and now a `Choices Model` all of which you basically did for me 🙂

Each question in each questionnaire has different answers `(Choices)` for which to choose.

When the Questionnaire is rendered all the questions are listed as select boxes.

I’ve updated the form and view to populate the questions related select box with the available choices, but at the moment i am stuck on how to filter each question to its choices as at the moment every select is displaying the choices from question 1 only.

**Form.py**

```auto
    def __init__ (self, *args, **kwargs):
         question_id = kwargs.pop('question_id')
         super(). __init__ (*args, **kwargs)
         self.fields['answer'] = forms.ModelChoiceField(
             queryset=Choice.objects.filter(question_id=question_id),
             widget=forms.Select(attrs={'class': 'form-control select2'})
         )

```

**View.py**

```auto
def get_questionnaire(request, project_id, questionnaire_id):

    # Get or Create the Response object for the parameters
    next = request.POST.get('next', '/')
    response, created = ProjectQuestionnaireResponse.objects.get_or_create(
        project_name_id=project_id, questionnaire_id=questionnaire_id
    )

    AnswerFormSet = modelformset_factory(ProjectQuestionnaireAnswer, form=AnswerForm, fields=('answer','notes',), extra=0)

    answer_queryset = ProjectQuestionnaireAnswer.objects.filter(response=response
    ).order_by('question__sequence'
    ).select_related('question')

    if request.method == 'POST':
        formset = AnswerFormSet(request.POST, form_kwargs={'question_id': 1})
        instances = formset.save()
        return HttpResponseRedirect(next)
    else:
    # Get the list of questions for which no Answer exists
        new_answers = ProjectQuestionnaireQuestion.objects.filter(
        questionnaire__projectquestionnaireresponse=response
        ).exclude(
        projectquestionnaireanswer__response=response
        )

    # This is safe to execute every time. If all answers exist, nothing happens
    for new_answer in new_answers:
        ProjectQuestionnaireAnswer(question=new_answer, response=response).save()

    answer_formset = AnswerFormSet(queryset=answer_queryset, form_kwargs={'question_id': 1})
                
    return render(request, 'project_evaluation.html', {'formset': answer_formset,'project_name':response.project_name,'title':response.questionnaire.title})

```

Now i know its this line that is the problem or at least one of possibly many problems.

`formset = AnswerFormSet(request.POST, form_kwargs={'question_id': 1})`

As this is returning the choices from `question 1` only.

Is there a better way/correct way to be doing this?

I’m thinking that i need a `for loop` in there somewhere so that the choices form part of the `for loop` for each of the questions.

```auto
{% csrf_token %}
{{ form.non_field_errors }}
{{ formset.management_form }}
{% for form in formset %}
<p class="mg-b-10"><span class="tx-warning si si-question mr-1" data-toggle="tooltip" title="{{form.instance.question.description}}"></span> {{ form.id }} {{ form.instance.question.question }}<span class="tx-danger ml-2">*</span></p>
<div class="parsley-checkbox d-flex mg-b-0 mb-3" id="cbWrapper2">
    {{ form.answer }}
</div><!-- parsley-checkbox -->
<div class="" id="cbErrorContainer2"></div>
{% endfor %}

```

Thanks again.

Tom.

---

<div class="post-metadata">

**Author:** ![KenWhitesell](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/kenwhitesell/32/280_2.png) [@KenWhitesell](https://forum.djangoproject.com/u/KenWhitesell)\
**Post date:** [March 20, 2022, 11:46am UTC](https://forum.djangoproject.com/t/django-model-form-different-choices/12493/31 "2022-03-20T11:46:38Z")

</div>

Ok, so the “twist” here is that you’re using a formset to generated the individual questions / choices.

A formset creates all the individual forms for you, and what you’re needing to do is modify each of those forms as they’re being created.

See `get_form_kwargs` in [Formsets | Django documentation | Django](https://docs.djangoproject.com/en/4.0/topics/forms/formsets/#passing-custom-parameters-to-formset-forms).

You may also find the discussion (and related links) at [Pass different parameters to each form in formset](https://forum.djangoproject.com/t/pass-different-parameters-to-each-form-in-formset/4040) to be helpful.

---

<div class="post-metadata">

**Author:** ![TommyQuality](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/tommyquality/32/6302_2.png) [@TommyQuality](https://forum.djangoproject.com/u/TommyQuality)\
**Post date:** [March 20, 2022, 5:29pm UTC](https://forum.djangoproject.com/t/django-model-form-different-choices/12493/32 "2022-03-20T17:29:52Z")

</div>

Thanks.

Reading over the links and the docs, i am lost. But will give this my best shot.

Would you mind just confirming that what i have already here is correct and i just need to build this out.

```auto
class AnswerForm(ModelForm):
    class Meta:
        model = ProjectQuestionnaireAnswer
        fields = ('answer','notes')

        widgets = {
            'notes': forms.Textarea(attrs={'class': 'form-control','placeholder': 'Add notes to question here'}),
        }

    def __init__ (self, *args, **kwargs):
         question_id = kwargs.pop('question_id')
         super(). __init__ (*args, **kwargs)
         self.fields['answer'] = forms.ModelChoiceField(
             queryset=Choice.objects.filter(question_id=question_id),
             widget=forms.Select(attrs={'class': 'form-control select2'})
         )

```

---

<div class="post-metadata">

**Author:** ![TommyQuality](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/tommyquality/32/6302_2.png) [@TommyQuality](https://forum.djangoproject.com/u/TommyQuality)\
**Post date:** [March 29, 2022, 6:41pm UTC](https://forum.djangoproject.com/t/django-model-form-different-choices/12493/33 "2022-03-29T18:41:34Z")

</div>

Finally got this passing in the choice for each question, but it won’t update the database on `POST`

```auto
    def __init__ (self, *args, **kwargs):
        instance = kwargs.get('instance')
        question_id = kwargs.pop('question_id')
        super(). __init__ (*args, **kwargs)
        self.fields['answer'] = forms.ModelChoiceField(
            queryset=Choice.objects.filter(question=instance),
            widget=forms.Select(attrs={'class': 'form-control select2'})
        )

```

```auto
    all_questions = ProjectQuestionnaireQuestion.objects.filter(questionnaire=questionnaire_id)    
    answer_formset = AnswerFormSet(queryset=all_questions, form_kwargs={'question_id': questionnaire_id})

```

Which i think is because of this

```auto
    if request.method == 'POST':
        formset = AnswerFormSet(request.POST, form_kwargs={'question_id': questionnaire_id})
        instances = formset.save()
        return HttpResponseRedirect(next)
    else:
...

```

There’s no error just doesn’t update, and i don’t know why ☹

---

<div class="post-metadata">

**Author:** ![KenWhitesell](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/kenwhitesell/32/280_2.png) [@KenWhitesell](https://forum.djangoproject.com/u/KenWhitesell)\
**Post date:** [March 29, 2022, 8:07pm UTC](https://forum.djangoproject.com/t/django-model-form-different-choices/12493/34 "2022-03-29T20:07:12Z")

</div>

You’re not using any “is\_valid” checks on your form - if you’ve got any form errors being submitted, they’re not being shown. You’re redirecting to “next” regardless of what’s happening in the submission. This makes it tough to diagnose any problems within the data being submitted or any other validation errors.

---

<div class="post-metadata">

**Author:** ![TommyQuality](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/tommyquality/32/6302_2.png) [@TommyQuality](https://forum.djangoproject.com/u/TommyQuality)\
**Post date:** [March 29, 2022, 8:31pm UTC](https://forum.djangoproject.com/t/django-model-form-different-choices/12493/35 "2022-03-29T20:31:54Z")

</div>

Ok. Thanks, Ken.

Im now getting an error  
`for new_answer in new_answers: UnboundLocalError: local variable 'new_answers' referenced before assignment `

I now see the value of `if_valid`

---

<div class="post-metadata">

**Author:** ![TommyQuality](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/tommyquality/32/6302_2.png) [@TommyQuality](https://forum.djangoproject.com/u/TommyQuality)\
**Post date:** [April 2, 2022, 8:59am UTC](https://forum.djangoproject.com/t/django-model-form-different-choices/12493/36 "2022-04-02T08:59:33Z")

</div>

Hi Ken,

I’ve managed to get the `formset` working now and onto the next piece which is to record a value for the choice selection.

My thinking was that I have a `choice_value` within my `Choices` model. choice\_value being `Red`, `Green` or `Amber` So when the answer (Choice) is selected i can also capture the choice\_value.

But cause I’m working with formsets this is more complicated.

I could present the choice\_value on the form, but hidden? But want the choice\_value to be hidden and not viewable in `page source` so was thinking I could somehow capture this in the function and `POST` with the `form.save()`

But i can’t figure out how to identify the query to get the value.

```auto
        value = formset.save(commit=False)
        formset.value = choice.choice_value
        instances = formset.save()

```

```auto
class Choice(models.Model):
    question = models.ForeignKey(ProjectQuestionnaireQuestion, on_delete=models.CASCADE)
    choice_text = models.CharField(max_length=200)
    choice_value = models.CharField(max_length=20, blank=True)
    choice_weight = models.IntegerField(blank=True, null=True)

    def __str__ (self):
        return str(self.choice_text)

```

```auto
class ProjectQuestionnaireAnswer(models.Model):
    YN_Choices = [
        ('Yes', 'Yes'),
        ('No', 'No'),
        ('Unknown', 'Unknown')
    ]
    question = models.ForeignKey(ProjectQuestionnaireQuestion, on_delete=models.CASCADE)
    answer = models.ForeignKey(Choice, on_delete=models.CASCADE,null=True)
    value = models.CharField(max_length=20, blank=True)
    notes = models.TextField(blank=True)
    response = models.ForeignKey(ProjectQuestionnaireResponse, on_delete=models.CASCADE)
    
    class Meta:
        constraints = [
                models.UniqueConstraint(fields=['question','response'], name='project_unique_response2'),
            ]

    def __str__ (self):
        return str(self.answer)

```

Any pointers would be great

Thanks

---

<div class="post-metadata">

**Author:** ![KenWhitesell](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/kenwhitesell/32/280_2.png) [@KenWhitesell](https://forum.djangoproject.com/u/KenWhitesell)\
**Post date:** [April 2, 2022, 10:42am UTC](https://forum.djangoproject.com/t/django-model-form-different-choices/12493/37 "2022-04-02T10:42:38Z")

</div>

There’s no need to record the values from the Choice model indepedently. You’ve got the recorded answers as an FK to the Choice model. Whenever you need those values from a supplied answer, you follow the FK reference to the original model.

---

<div class="post-metadata">

**Author:** ![TommyQuality](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/tommyquality/32/6302_2.png) [@TommyQuality](https://forum.djangoproject.com/u/TommyQuality)\
**Post date:** [April 2, 2022, 11:03am UTC](https://forum.djangoproject.com/t/django-model-form-different-choices/12493/38 "2022-04-02T11:03:16Z")

</div>

Of course. 🙂  
Thanks Ken.

[Previous page](https://forum.djangoproject.com/t/django-model-form-different-choices/12493.md?page=1)
