2

I'm trying to add a dropdown box of choices in my form. What I tried:

from django import forms
from .models import Car

class CarForm(forms.ModelForm):
    owner_name = forms.CharField(max_length=100, label='Owner Name', required=True)
    car_type = forms.ChoiceField(choices=Car.CarTypes.choices, label='Car Type', required=True)

    class Meta:
        model = Car
        fields = ('owner_name', 'car_type')

It adds the car_type field to the form but it's not a dropdown for some reason (it's a regular empty box you could fill). The CarTypes looks like:

    class CarTypes(models.TextChoices):
        X = 'some_x'
        Y = 'somy_y'
        # ...

What could be the reason?

In my Car class I have: car_type = models.CharField(max_length=24, choices=CarTypes.choices, default=CarTypes.X). Could be it? I think CharField makes it empty box. But changing it to ChoiceField ends with: module 'django.db.models' has no attribute 'ChoiceField'. How to solve it?

I tried this approach but it uses a tuple instead of a class. From the example there I see they use tuple of two-value-tuples like (('a','A'),('b','B')) and I'm using class. Could it be the reason?

In my html I have:

    <form method="post" class="form-group">
      {% csrf_token %}
      {% for field in form %}
        <div class="form-group col-md-12 mb-3">
          <label for="{{ field.label }}">{{ field.label }}</label>
          <input type="text" class="form-control" id="{{ field.label }}" name="{{ field.name }}">
        </div>
      {% endfor %}
      <hr class="mb-4">
      <button type="submit" class="btn btn-secondary btn-lg btn-block">Submit New Car</button>
    </form>

And the Car looks like:


class Car(models.Model):

    class CarTypes(models.TextChoices):
        X = 'some_x'
        Y = 'somy_y'
        # ...

    owner_name = models.CharField(max_length=100,unique=True)
    car_type = models.CharField(max_length=24, choices=CarTypes.choices, default=CarTypes.X)

    def __str__(self):
        return self.owner_name
1
  • Share also your model Car. Django vesion? Commented Feb 21, 2021 at 12:57

3 Answers 3

3

You don't need to override your model fields. See Field types - Django Docs

If the model field has choices set, then the form field’s widget will be set to Select, with choices coming from the model field’s choices.

The following code should do what you want:

forms.py

from django import forms
from .models import Car

class CarForm(forms.ModelForm):
    class Meta: 
        model = Car 
        fields = ['owner_name', 'car_type']

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['owner_name'].widget.attrs.update({"class": "form-control"})
        # or iterate over field to add class for each field
        for field in self.fields:
            self.fields[field].widget.attrs.update({'class':"form-control"})

You can add any html attribute in __init__ method.

html:

<form method="post" class="form-group">
  {% csrf_token %}
    <div class="form-group col-md-12 mb-3">
      <label for="{{ form.owner_name.label }}">{{ form.owner_name.label }}</label>
      {{ form.owner_name }}
    </div>

    <div class="form-group col-md-12 mb-3">
        <label for="{{ form.car_type.label }}">{{ form.car_type.label }}</label>
        {{ form.car_type }}
    </div>
  <hr class="mb-4">
  <button type="submit" class="btn btn-secondary btn-lg btn-block">Submit New Car</button>
</form>

Or You can use {{ form.as_p }} - Django Docs will render them wrapped in <p> tags

UPDATE


Render form manually:

<form method="post" class="form-group">
    {% csrf_token %}
    <div class="form-group col-md-12 mb-3">
        <label for="{{ form.owner_name.label }}">{{ form.owner_name.label }}</label>
        <input type="text" class="form-control" id="{{ form.owner_name.auto_id }}" name="{{ form.owner_name.name }}">
    </div>

    <div class="form-group col-md-12 mb-3">
        <label for="{{ form.car_type.label }}">{{ form.car_type.label }}</label>
        <select id="{{ form.car_type.auto_id }}" name="{{ form.car_type.name }}">
        {% for value, label in form.fields.car_type.choices %}
            <option value="{{ value }}"{% if form.car_type.value == value %} selected{% endif %}>{{ label }}</option>
        {% endfor %}
        </select>
    </div>
  <hr class="mb-4">
  <button type="submit" class="btn btn-secondary btn-lg btn-block">Submit New Car</button>
</form>
Sign up to request clarification or add additional context in comments.

7 Comments

I used {{ field }} instead of my input part. But how do I customize it with class="form-control" id="{{ field.label }}" name="{{ field.name }}"?
@vesii, You can add any html attribute in init method.
Is it possible to do in the HTML itself?
You have to render your input than manually.
Also, do you have an idea why __init__ is not being executed? Added prints both in Meta and in __init__ but __init__ does not print anything (and does not add the form-control).
|
1

Try to use this method of Choices :-

models.py

choices = [
    ('choice1', 'choice1'),
    ('choice2',"choice2"),
    ('choice3',"choice3")
]

class Car(models.Model):

    owner_name = models.CharField(max_length=100,unique=True)
    car_type = models.CharField(max_length=24, choices=choices, default='choice1')

    def __str__(self):
        return self.owner_name

And then don't forget to make migrations

Refer this ---> https://docs.djangoproject.com/en/3.0/ref/models/fields/#django.db.models.Field.choices

3 Comments

I get TypeError: __init__() got an unexpected keyword argument 'choices' because forms.ModelForm does not have choices in CharField.
How do you keep it class though?
Just remove the choice Class CarTypes in your existing model.
0

For Django 3.1, you can achieve this with widgets. Here's how you can get it done:

from django.forms import ModelForm, Select, TextInput
from .models import Car

class CarForm(ModelForm):
    class Meta:
        model = Car
        fields = ('owner_name', 'car_type')
        widgets = {
            'owner_name': TextInput(),
            'car_type': Select(),
        }

And Model should look like this:

    class Car(models.Model):
        choices = [('X':'some_x'), ('Y': 'some_y']
        owner_name = models.CharField(max_length=100,unique=True)
        car_type = models.CharField(max_length=24, choices=choices, default='some_y')

        def __str__(self):
            return self.owner_name```

1 Comment

Does forms.ChoiceField ModelForm have choices? From the second answer I got TypeError: __init__() got an unexpected keyword argument 'choices' because forms.ModelForm does not have choices in CharField

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.