Django provides flexible ways to work with choices. In this article, we will cover the next topics:
- Django model's choices attribute
- forms.ModelChoiceField
- forms.ChoiceField
Working with Field.choices
in Django Models
Let's assume we have the following model, which represents a developer:
class Developer(models.Model):
JUNIOR = 'JR'
MIDDLE = 'ML'
SENIOR = 'SR'
LEVEL_CHOICES = (
(JUNIOR, 'Junior'),
(MIDDLE, 'Middle'),
(SENIOR, 'Senior'),
)
name = models.CharField(max_length=200)
level = models.CharField(max_length=2, choices=LEVEL_CHOICES, default=JUNIOR)
The first value of each tuple inside LEVEL_CHOICES
is stored in the related table's level
column and the second value is the human-readable version. It's best practice to define choices inside the model class and constants (JUNIOR, MIDDLE, SENIOR
) which contain a choice's actual value. So, we can use one of the constants to mark as the default value.
The default widget of the field with defined choices is the select/option box instead of text input.
And actual values ("JR") are assigned to the value
attribute of the option
HTML tag.
Also, Django allows defining grouped choices. It allows combining grouped options with the ungrouped options, such as “Unknown” in the example below:
class Developer(models.Model):
...
CITY_CHOICES = [
('US', (
('ATL', 'Atlanta'),
('BOS', 'Boston'),
)
),
('UK', (
('BAT', 'Bath'),
('BRI', 'Bristol'),
)
),
('UN', 'Unknown'),
]
name = models.CharField(max_length=200)
level = models.CharField(max_length=2, choices=LEVEL_CHOICES)
city = models.CharField(max_length=3, choices=CITY_CHOICES)
For each model field that has choices
attribute set, Django adds a helper method to display a human-readable version of the stored value. This method has the form obj.get_FOO_display()
, where FOO
is the field name. The following example makes it clear:
>>> d = Developer.objects.first()
>>> d.level
'SR'
>>> d.get_level_display()
'Senior'
>>> d.city
'ATL'
>>> d.get_city_display()
'Atlanta'
Foreign Key field as a choice field in the form
Now, developers can have a mentor. So we add a new ForeignKey field which refers to the same model.
class Developer(models.Model):
...
name = models.CharField(max_length=200)
level = models.CharField(max_length=2, choices=LEVEL_CHOICES, default=JUNIOR)
city = models.CharField(max_length=3, choices=CITY_CHOICES)
mentor = models.ForeignKey('self', on_delete=models.PROTECT, null=True, blank=True)
def __str__(self):
return f"{self.name} - {self.level}"
Next, add a view in which admins can add a new developer by assigning an appropriate mentor.
Our forms.py
looks the following way:
from django import forms
from .models import Developer
class DeveloperAddForm(forms.ModelForm):
class Meta:
model = Developer
fields = (
'name',
'level',
'mentor',
)
Let's assume only the Senior developer can be a mentor, so we have to display in the select options only Senior level developer names. How can we do that?
Well, it's not so difficult. The form's field mentor
is automatically pulled from the related model, when we included it inside Meta
class's fields
attribute.
Django form ModelChoiceField
To display only Senior developers we need to customize our form field mentor
and here we introduce forms.ModelChoiceField
. This field has queryset
attribute which can be assigned queryset with an extra filter/exclude logic. In our case, we filter mentors by level, so that only senior programmers can be displayed in the mentor select box.
class DeveloperAddForm(forms.ModelForm):
mentor = forms.ModelChoiceField(
queryset=Developer.objects.filter(level=Developer.SENIOR)
)
class Meta:
---
As you can see, it displays only Senior developers. One thing to note, the option tags value contains the id of the Developer model (primary key).
Django forms ChoiceField
Django forms also have another type of ChoiceField. It is not attached to any model queryset. For example, to add a age
select box to our form without adding such a choice field into our Developer
model, we can add the field directly to the form. AGE_CHOICES
is the tuple that contains inner tuples and it is assigned to the choices attribute of the forms.ChoiceField
.
class DeveloperAddForm(forms.ModelForm):
AGE_CHOICES = (
(1, 'Under 18'),
(2, '19-24'),
(3, '25-35'),
(4, 'Older than 35')
)
mentor = forms.ModelChoiceField(
queryset=Developer.objects.filter(level=Developer.SENIOR)
)
age = forms.ChoiceField(
choices=AGE_CHOICES
)
class Meta:
---
As we can see in the next image, the field is converted to the nice select/option box:
The value of the option tags is the actual value in the AGE_CHOICES
tuple, which is the first value of each inner tuple.
Hope, this guide helps you to better understand how different forms of ChoiceField work in the Django framework.
Happy coding!