Choice items

The ChoiceItem class is what drives the choices. Each instance corresponds to a possible choice for your field.

Basic usage

class MyChoices(DjangoChoices):
    my_choice = ChoiceItem(1, 'label 1')

The first argument for ChoiceItem is the value, as it will be stored in the database. ChoiceItem values can be any type, as long as it matches the field where the choices are defined, e.g.:

String type:

class Strings(DjangoChoices):
    one = ChoiceItem('one', 'one')


class Model1(models.Model):
    field = models.CharField(max_length=10, choices=Strings.choices)

or integer:

class Ints(DjangoChoices):
    one = ChoiceItem(1, 'one')


class Model2(models.Model):
    field = models.IntegerField(choices=Ints.choices)

There is also a ‘short name’. You can import C instead of ChoiceItem, if you’re into that.

Custom attributes

Any additional (custom) keyword arguments passed to the constructor, are made available as custom attributes:

>>> choice = ChoiceItem('excellent', limit_to=['US', 'CA', 'CN'])
>>> choice.limit_to
['US', 'CA', "CN"]

To obtain the ChoiceItem instance, see get_choice

Labels

The second argument to the ChoiceItem class is the label. It’s recommended to specify this explicitly if you use internationalization, e.g.:

from django.utils.translation import ugettext_lazy as _


class MyChoices(DjangoChoices):
    one = ChoiceItem(1, _('one'))

If the label is not provided, it will be automatically determined from the class property, and underscores are translated to spaces. So, the following example yields:

>>> class MyChoices(DjangoChoices):
...     first_choice = ChoiceItem(1)

>>> MyChoices.choices
((1, 'first choice'),)

Ordering

ChoiceItem objects also support ordering. If not provided, the choices are returned in order of declaration.

>>> class MyChoices(DjangoChoices):
...     first = ChoiceItem(1, order=20)
...     second = ChoiceItem(2, order=10)

>>> MyChoices.choices
(
    (2, 'second'),
    (1, 'first'),
)

Values

If you really want to use the minimal amount of code, you can leave off the value as well, and it will be determined from the label.

>>> class Sample(DjangoChoices):
...     OptionA = ChoiceItem()
...     OptionB = ChoiceItem()

>>> Sample.choices
(
    ('OptionA', 'OptionA'),
    ('OptionB', 'OptionB'),
)

DjangoChoices class attributes

The choices class itself has a few useful attributes. Most notably choices, which returns the choices as a tuple.

choices

>>> class Sample(DjangoChoices):
...     OptionA = ChoiceItem()
...     OptionB = ChoiceItem()

>>> Sample.choices
(
    ('OptionA', 'OptionA'),
    ('OptionB', 'OptionB'),
)

labels

Returns a dictionary with a mapping from attribute to the human-readable label:

>>> class MyChoices(DjangoChoices):
...     first_choice = ChoiceItem(1)
...     second_choice = ChoiceItem(2)

>>> MyChoices.labels
{'first_choice': 1, 'second_choice': 2}
>>> MyChoices.labels.first_choice
"first choice"

values

Returns a dictionary with a mapping from value to label:

>>> class MyChoices(DjangoChoices):
...     first_choice = ChoiceItem(1, 'label 1')
...     second_choice = ChoiceItem(2, 'label 2')

>>> MyChoices.values
{1: 'label 1', '2': 'label 2'}

validator

Note

At least since Django 1.3, there is model and form-level validation of the choices. Unless you have a reason to explicitly specify/override the validator, you can skip specifying this validator.

Returns a validator that can be used in your model field. This validator checks that the value passed to the field is indeed a value specified in your choices class.

attributes

Returns an OrderedDict with the mapping from choice value -> attribute on the choices class.

>>> class MyChoices(DjangoChoices):
...     first_choice = ChoiceItem(1, 'label 1')
...     second_choice = ChoiceItem(2, 'label 2')

>>> MyChoices.attributes
OrderedDict([(1, 'first_choice'), (2, 'second_choice')])

get_choice

Returns the actual ChoiceItem instance for a given value:

>>> class MyChoices(DjangoChoices):
...     first_choice = ChoiceItem(1, 'label 1')
...     second_choice = ChoiceItem(2, 'label 2')

>>> MyChoices.get_choice(MyChoices.second_choice)
<ChoiceItem value=2 label='label 2' order=1>

This allows you to inspect any ChoiceItem attributes.

get_order_expression

Build the Case/When statement to use in queryset annotations.

Choices defined get an implicit or explicit order, which can have semantic value. This ORM expression allows you to map choice values to the order value, as an integer, on the database level.

It is then available for subsequent filtering, allowing more work to be done in the database instead of in Python.

>>> class MyChoices(DjangoChoices):
...     first = ChoiceItem('first')
...     second = ChoiceItem('second')

>>> order = MyChoices.get_order_expression('some_field')
>>> queryset = Model.objects.annotate(some_field_order=order)
>>> for item in queryset:
...     print(item.some_field)
...     print(item.some_field_order)
# first_
# 1
# second
# 2