# 'Please choose correct value' in ModelForm

**URL:** <https://forum.djangoproject.com/t/please-choose-correct-value-in-modelform/15752>\
**Category:** Forms & APIs\
**Created:** [September 1, 2022, 5:45am UTC](https://forum.djangoproject.com/t/please-choose-correct-value-in-modelform/15752 "2022-09-01T05:45:01Z")\
**Posts on this page:** 13\
**Page:** 1

<div class="post-metadata">

**Author:** ![Sweetbarrow](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/sweetbarrow/32/9633_2.png) [@Sweetbarrow](https://forum.djangoproject.com/u/Sweetbarrow)\
**Post date:** [September 1, 2022, 5:45am UTC](https://forum.djangoproject.com/t/please-choose-correct-value-in-modelform/15752/1 "2022-09-01T05:45:01Z")

</div>

I have a model have `ForeignKey` and I want to use HTML `<datalist>` in ModelForm.  
But the value in `<option>` should be key of `ForeignKey`, not the ` __str__ ` or something else.  
I can use `label` for `<option>` but, it rendered different by browser.  
So, I tried to override `clean` or `clean_<fieldname>` method, but I don’t know how to handle input from there.  
How can I intercept form input and fix it to correct value?

**models.py**

```auto
from django.conf import settings
from django.db import models

# Create your models here.
class Schedule(models.Model):
    srl = models.BigAutoField(
        primary_key=True,
        verbose_name="Serial",
    )
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        verbose_name="User",
    )
    branch = models.ForeignKey(
        "branches.Branch",
        on_delete=models.CASCADE,
        verbose_name="Branch",
    )
    date = models.DateField(
        verbose_name="Date",
    )
    period = models.DecimalField(
        max_digits=2,
        decimal_places=0,
        verbose_name="Period",
    )

    class Meta:
        verbose_name = "Schedule"
        verbose_name_plural = "Schedules"
        ordering = [
            "branch",
            "date",
            "period",
        ]
        unique_together = [
            "user",
            "date",
            "period",
        ]

    def __str__ (self):
        if self.user.gender == "M":
            gender_short = "M"
        elif self.user.gender == "F":
            gender_short = "F"

        return f"{self.branch} {self.date.strftime('%y%m%d')} {self.period}th {self.user.name}{self.user.birthday.strftime('%y%m%d')}{gender_short}"

```

**forms.py**

```auto
import datetime

from django import forms
from django.forms import ModelChoiceField, ModelForm

from branches.models import Branch
from schedules.models import Schedule
from timetables.models import Timetable
from users.models import User

class ScheduleForm(ModelForm):
    def __init__ (self, *args, **kwargs):
        self.user = kwargs.pop("user")
        super(ScheduleForm, self). __init__ (*args, **kwargs)
        if self.user.superuser:
            self.fields["branch"] = ModelChoiceField(
                queryset=Branch.objects.all(),
                required=True,
                label="Branch",
                widget=forms.Select(
                    attrs={
                        "class": "form-select",
                    }
                ),
            )
        else:
            self.fields["branch"] = ModelChoiceField(
                queryset=Branch.objects.filter(branch=self.user.branch),
                required=True,
                label="Branch",
                widget=forms.Select(
                    attrs={
                        "class": "form-select",
                    }
                ),
            )

    def clean(self):
        cleaned_data = super(ScheduleForm, self).clean()
        user_input = cleaned_data["user"]
        name = user_input.split(" ")[0]
        user_info = user_input.split(" ")[1]
        user_info = user_info.strip("()")
        user_info = user_info.split("/")
        branch = Branch.objects.get(name=user_info[0])
        birthday = datetime.datetime.strptime(user_info[1], "%y%m%d")
        if user_info[2] == "M":
            gender = "M"
        else:
            gender = "F"
        user = User.objects.get(
            name=name,
            birthday=birthday,
            gender=gender,
            branch=branch,
        )
        cleaned_data["user"] = user
        return cleaned_data

    class Meta:
        model = Schedule
        fields = (
            "branch",
            "user",
            "date",
            "period",
        )
        widgets = {
            "user": forms.TextInput(
                attrs={
                    "class": "form-control",
                    "list": "user-list",
                },
            ),
            "date": forms.DateInput(
                attrs={
                    "type": "date",
                    "class": "form-control",
                },
            ),
            "period": forms.NumberInput(
                attrs={
                    "class": "form-control",
                    "min": 1,
                },
            ),
        }

```

**views.py**

```auto
...
class ScheduleCreateView(LoginRequiredMixin, CreateView):
    model = Schedule
    form_class = ScheduleForm
    success_url = reverse_lazy("schedules:list")
    login_url = reverse_lazy("users:login")

    def get_form_kwargs(self):
        kwargs = super(ScheduleCreateView, self).get_form_kwargs()
        kwargs.update({"user": self.request.user})
        return kwargs

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context["page_title"] = "Add Schedule"
        if self.request.user.superuser:
            context["user_list"] = User.objects.all()
        else:
            context["user_list"] = User.objects.filter(branch=self.request.user.branch)

        return context

```

* * *

I add `print(locals())` to `clean()` method after `super().clean()` and there is no `user`.  
What’s the problem?

---

<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:** [September 1, 2022, 2:18pm UTC](https://forum.djangoproject.com/t/please-choose-correct-value-in-modelform/15752/2 "2022-09-01T14:18:16Z")

</div>

How are you rendering the datalist? What does your template for that look like?

---

<div class="post-metadata">

**Author:** ![Sweetbarrow](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/sweetbarrow/32/9633_2.png) [@Sweetbarrow](https://forum.djangoproject.com/u/Sweetbarrow)\
**Post date:** [September 2, 2022, 1:17am UTC](https://forum.djangoproject.com/t/please-choose-correct-value-in-modelform/15752/3 "2022-09-02T01:17:16Z")

</div>

I render datalist with including list of `QuerySet` of `User` objects in `get_context_data()` in `views.py` like the code below.

```auto
def get_context_data(self, **kwargs):
    context = super().get_context_data(**kwargs)
    context["page_title"] = "Add schedule"
    if self.request.user.superuser:
        context["user_list"] = User.objects.all()
    else:
        context["user_list"] = User.objects.filter(branch=self.request.user.branch)

    return context

```

Here is the form template that I’m using.  
**schedule\_form.html**

```auto
<!DOCTYPE html>
{% extends "main/base.html" %}
{% load static %}
{% block content %}
    <form action="" method="POST" class="container d-grid gap-3 justify-content-evenly align-items-center">
        {% csrf_token %}
        {{ form.non_field_errors }}
        <div class="row">
            <label class="col-3 col-form-label text-nowrap" for="{{ form.user.id_for_label }}">{{ form.user.label }}</label>
            <div class="col">{{ form.user }}</div>
            {{ form.user.errors }}
        </div>
        
        {% if user_list %}
            <datalist id="user-list">
                {% for user in user_list %}
                    <option value="{{ user }}">
                {% endfor %}
            </datalist>
        {% endif %}
        
        <div class="row">
            <label class="col-3 col-form-label text-nowrap" for="{{ form.branch.id_for_label }}">{{ form.branch.label }}</label>
            <div class="col">{{ form.branch }}</div>
            {{ form.branch.errors }}
        </div>
        
        <div class="row">
            <label class="col-3 col-form-label text-nowrap" for="{{ form.date.id_for_label }}">{{ form.date.label }}</label>
            <div class="col">{{ form.date }}</div>
            {{ form.date.errors }}
        </div>
        
        <div class="row">
            <label class="col-3 col-form-label text-nowrap" for="{{ form.period.id_for_label }}">{{ form.period.label }}</label>
            <div class="col">{{ form.period }}</div>
            {{ form.period.errors }}
        </div>
        
        <div class="row">
            <div class="col d-flex justify-content-end">
                <button class="btn btn-primary" type="submit">저장</button>
            </div>
        </div>
    </form>
{% endblock %}

```

For your information, I tried replacing `<option value="{{ user }}">` with `<option value="{{ user.srl }} label={{ user }}">`.  
This works well without overriding any method, but this one shows `user.srl` as major and ` __str__ ` of `User` object as additional information in MS Edge,

---

<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:** [September 2, 2022, 1:26am UTC](https://forum.djangoproject.com/t/please-choose-correct-value-in-modelform/15752/4 "2022-09-02T01:26:51Z")

</div>

I’m still not quite following what you _want_ to have happen here.

What do you want the `<option>` elements in the datalist to look like? (Specifically - please post the html of an `option` element as you would want it rendered.)

---

<div class="post-metadata">

**Author:** ![Sweetbarrow](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/sweetbarrow/32/9633_2.png) [@Sweetbarrow](https://forum.djangoproject.com/u/Sweetbarrow)\
**Post date:** [September 2, 2022, 1:40am UTC](https://forum.djangoproject.com/t/please-choose-correct-value-in-modelform/15752/5 "2022-09-02T01:40:16Z")

</div>

I want to keep `<option value="{{ user }}">` this one for cosmetic reason.  
For example, let’s assume there is an object of the user ‘John Doe’, who belong to branch 1 and his birthday is 2022-09-02.  
This object’s `pk` or `srl` will be `1` here.

There is `def __str__ ` defined in the `User` model like this

```auto
def __str__ (self):
    return f"{name} ({branch}/{birthday}/{gender})"

```

When users try to add schedule, `datalist` shows list like `John Doe (Branch 1/220902/Male)`.  
But Django can’t handle this directly because the value of the option is string, not the `User` object or `pk` of `User` object.  
So, what I know that can handle this is change HTML `option` tag into `<option value="{{ user.srl }} label={{ user }}">` this.  
But this not showing list like I want.  
This shows `1` instead of `John Doe (Branch 1/220902/Male)`.  
I tried to override `clean()` to change `John Doe (Branch 1/220902/Male)` this to correct `User` object, but form doesn’t pass this input.

* * *

In conclusion, what I want in HTML is `<option value="{{ user }}">`.  
But I don’t know how to handle this in Django.

---

<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:** [September 2, 2022, 1:52am UTC](https://forum.djangoproject.com/t/please-choose-correct-value-in-modelform/15752/6 "2022-09-02T01:52:02Z")

</div>

First, you can’t, under _any_ circumstance, pass an “object” through the template to the browser. It’s always going to be a string. Whether that string is a representation of the object or a field of the object doesn’t matter. You’re always passing a string through the form.

If you define your form field to be a CharField that is _not_ directly associated with the model, the form will return that string to the form.

In other words, you need to define that field as an “extra” field and not as one of the fields automatically created by the model form. Remove it from the `fields` list and remove the widgets definition from Meta. Define it as a normal form field as a CharField to accept the input, and then in your processing of the form, find the related user object and set the Model field to the corresponding value.

---

<div class="post-metadata">

**Author:** ![Sweetbarrow](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/sweetbarrow/32/9633_2.png) [@Sweetbarrow](https://forum.djangoproject.com/u/Sweetbarrow)\
**Post date:** [September 2, 2022, 2:02am UTC](https://forum.djangoproject.com/t/please-choose-correct-value-in-modelform/15752/7 "2022-09-02T02:02:45Z")

</div>

Can you check my understanding is correct?  
I can’t do what I want with current code (the one that I uploaded here).  
So, I need to remove `user` from `Meta`.  
Next, I need define other field to get input from the user in the ` __init__ ` of `ModelForm`.  
Then, I can handle this extra field with `clean_<field>()` or something.  
This makes using `datalist` available.  
Right?

---

<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:** [September 2, 2022, 2:16am UTC](https://forum.djangoproject.com/t/please-choose-correct-value-in-modelform/15752/8 "2022-09-02T02:16:39Z")

</div>

A ModelForm **is** a **Form**.

It is a form where Django creates some fields automatically based upon the Model defined for it.

A form with a ForeignKey field in the form is expecting to get the PK of that related object.

You are not supplying the PK.

Therefore, you _must_ not use that field in your form as defined by the model.

Which means:

> [@Sweetbarrow](#):
>
> So, I need to remove `user` from `Meta`.

Yes, that’s what you need to do.

You need to define what you’re going to accept as a field just like in any other form.

> [@Sweetbarrow](#):
>
> Next, I need define other field to get input from the user in the ` __init__ ` of `ModelForm`.

I don’t know what you are trying to say by this.

> [@Sweetbarrow](#):
>
> Then, I can handle this extra field with `clean_<field>()` or something.

You have at least three ways of handling this that I can see.

- `clean_user()` - you’re still going to need to assign the input to the model’s field for that object.
- `to_python()` for that field - you could define that field as a ForeignKey field using a TextInput widget, and use the `to_python` method on that field to convert the text string input to the ForeignKey reference.
- In the view _after_ the form has been processed - find the FK value to assign it to the model to be saved.

> [@Sweetbarrow](#):
>
> This makes using `datalist` available.

In the way that you want to use it yes.

---

<div class="post-metadata">

**Author:** ![Sweetbarrow](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/sweetbarrow/32/9633_2.png) [@Sweetbarrow](https://forum.djangoproject.com/u/Sweetbarrow)\
**Post date:** [September 2, 2022, 2:22am UTC](https://forum.djangoproject.com/t/please-choose-correct-value-in-modelform/15752/9 "2022-09-02T02:22:46Z")

</div>

> [@KenWhitesell](#):
>
> I don’t know what you are trying to say by this.

Isn’t it the place that I should define extra field like

```auto
class ScheduleForm(ModelForm):
    def __init(self):
        self.fields["user"] = forms.TextField()

```

this?

---

<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:** [September 2, 2022, 2:23am UTC](https://forum.djangoproject.com/t/please-choose-correct-value-in-modelform/15752/10 "2022-09-02T02:23:25Z")

</div>

No.

How would you define any field in any normal form?

---

<div class="post-metadata">

**Author:** ![Sweetbarrow](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/sweetbarrow/32/9633_2.png) [@Sweetbarrow](https://forum.djangoproject.com/u/Sweetbarrow)\
**Post date:** [September 2, 2022, 2:26am UTC](https://forum.djangoproject.com/t/please-choose-correct-value-in-modelform/15752/11 "2022-09-02T02:26:27Z")

</div>

Ah.  
Just `user = forms.TextField()`  
Right?

---

<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:** [September 2, 2022, 2:27am UTC](https://forum.djangoproject.com/t/please-choose-correct-value-in-modelform/15752/12 "2022-09-02T02:27:48Z")

</div>

Correct.  
(At the class level, not within a function or inner class)

---

<div class="post-metadata">

**Author:** ![Sweetbarrow](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/sweetbarrow/32/9633_2.png) [@Sweetbarrow](https://forum.djangoproject.com/u/Sweetbarrow)\
**Post date:** [September 2, 2022, 2:32am UTC](https://forum.djangoproject.com/t/please-choose-correct-value-in-modelform/15752/13 "2022-09-02T02:32:45Z")

</div>

Thanks.  
I will try it.

* * *

Thank you very much.  
Thanks to your answer, myt problem has solved.
