Lucid Polygon
Lucid Polygon

Reputation: 562

Django - Select a valid choice. That choice is not one of the available choices

Here are my forms:

class RoleForm(forms.ModelForm):
    class Meta:
        model = models.RoleModel
        fields = ['name']

class FeatureForm(forms.ModelForm):
    role = forms.ModelChoiceField(queryset=models.RoleModel.objects.values_list('name', flat=True).distinct())
    class Meta:
        model = models.FeatureModel
        fields = ['role','feature']

In my bootstrap form, the choices display properly. I get a list of all roles. But if I fill the feature form and hit submit it says - "Select a valid choice. That choice is not one of the available choices."

My models are:

class RoleModel(models.Model):
    name = models.CharField(validators=[alphanumeric], max_length=50, unique=True, blank=False)

class FeatureModel(models.Model):
    role = models.ForeignKey(RoleModel, on_delete=models.PROTECT)
    feature = models.CharField(validators=[alphanumeric], max_length=10, unique=True)

my bootsrtap form is:

<form action="{% url 'feature_form' %}" novalidate method="POST">{% csrf_token %}         
            <div class="row">
                <div class="col">
                <label for="role">{{ fform.role.label }}</label>
                <p><select class="form-control id="role" name="role">
                {% for item in fform.role %}
                {{ item }}
                {% endfor %}
                </select></p>
                {% for error in fform.role.errors %}
                <p><small class="alert-danger">{{ error }}</small></p>
                {% endfor %}
                </div>

                <div class="col">
                <label for="feature">{{ fform.feature.label }</label>
                <p><input type="text" class="form-control" id="feature" name="feature"
                {% if fform.feature.value is not None %}
                value="{{ fform.feature.value }}"
                {% endif %}
                ></p>
                {% for error in fform.feature.errors %}
                <p><small class="alert-danger">{{ error }}</small></p>
                {% endfor %}
                </div>
            </div>

            <input class='btn btn-primary btn-sm' type='submit' value='Save'>                
        </form>

My need is simple. The second form (FeatureForm) has two fields. role being foreign key of another model and a text field to type in name of a feature. On my client side, I need the foreign key to be displayed as a select option with a list. I chose a value from this select, enter the value of feature and hit save. It has to save.

client side screenshot

Upvotes: 1

Views: 6753

Answers (1)

Borut
Borut

Reputation: 3364

It doesn't work, because your queryset includes only names, but you need an id / primary key of a RoleModel. Since your choices don't have an id, they aren't a valid choice.

Firstly, your RoleModel name is set to unique and is therefore no point in querying distinct() name values because they will be distinct already by unique definition.

You also don't need to construct your own select input. Django will take care of this.

All you need is:

class RoleModel(models.Model):
    name = models.CharField(validators=[alphanumeric], max_length=50, unique=True, blank=False)

    def __str__(self):
         return self.name

class FeatureForm(forms.ModelForm):
    class Meta:
        model = models.FeatureModel
        fields = ['role', 'feature']

    def __init__(self, *args, **kwargs):
        super(FeatureForm, self).__init__(*args, **kwargs)
        for field in self.fields:
            self.fields[field].widget.attrs = {'class': 'form-control'}

and

<form action="{% url 'feature_form' %}" novalidate method="POST">
    {% csrf_token %}         
    <div class="row">
        <div class="col">
            <label for="role">{{ fform.role.label }}</label>
            <p>{{ fform.role }} </p>
            {% for error in fform.role.errors %}
            <p><small class="alert-danger">{{ error }}</small></p>
            {% endfor %}
        </div>
        <div class="col">
            <label for="feature">{{ fform.feature.label }</label>
            <p>{{ fform.feature }}</p>
            {% for error in fform.feature.errors %}
            <p><small class="alert-danger">{{ error }}</small></p>
            {% endfor %}
        </div>
    </div>
    <input class='btn btn-primary btn-sm' type='submit' value='Save'>                
</form>

EDIT: Otherwise, a Select input in this case should be constructed as:

<select>
    <option value="1">Name 1</option>
    <option value="2">Name 2</option>
</select>

where value is an id, a primary key of RoleModel in your case. Your select options don't have this value.

Upvotes: 2

Related Questions