summaryrefslogtreecommitdiff
path: root/docs/ref/models/fields.txt
diff options
context:
space:
mode:
Diffstat (limited to 'docs/ref/models/fields.txt')
-rw-r--r--docs/ref/models/fields.txt111
1 files changed, 108 insertions, 3 deletions
diff --git a/docs/ref/models/fields.txt b/docs/ref/models/fields.txt
index 01a56c1312..49494186ce 100644
--- a/docs/ref/models/fields.txt
+++ b/docs/ref/models/fields.txt
@@ -94,6 +94,7 @@ and the second element is the human-readable name. For example::
('SO', 'Sophomore'),
('JR', 'Junior'),
('SR', 'Senior'),
+ ('GR', 'Graduate'),
]
Generally, it's best to define choices inside a model class, and to
@@ -106,11 +107,13 @@ define a suitably-named constant for each value::
SOPHOMORE = 'SO'
JUNIOR = 'JR'
SENIOR = 'SR'
+ GRADUATE = 'GR'
YEAR_IN_SCHOOL_CHOICES = [
(FRESHMAN, 'Freshman'),
(SOPHOMORE, 'Sophomore'),
(JUNIOR, 'Junior'),
(SENIOR, 'Senior'),
+ (GRADUATE, 'Graduate'),
]
year_in_school = models.CharField(
max_length=2,
@@ -119,7 +122,7 @@ define a suitably-named constant for each value::
)
def is_upperclass(self):
- return self.year_in_school in (self.JUNIOR, self.SENIOR)
+ return self.year_in_school in {self.JUNIOR, self.SENIOR}
Though you can define a choices list outside of a model class and then
refer to it, defining the choices and names for each choice inside the
@@ -127,6 +130,95 @@ model class keeps all of that information with the class that uses it,
and makes the choices easy to reference (e.g, ``Student.SOPHOMORE``
will work anywhere that the ``Student`` model has been imported).
+In addition, Django provides enumeration types that you can subclass to define
+choices in a concise way::
+
+ from django.utils.translation import gettext_lazy as _
+
+ class Student(models.Model):
+
+ class YearInSchool(models.TextChoices):
+ FRESHMAN = 'FR', _('Freshman')
+ SOPHOMORE = 'SO', _('Sophomore')
+ JUNIOR = 'JR', _('Junior')
+ SENIOR = 'SR', _('Senior')
+ GRADUATE = 'GR', _('Graduate')
+
+ year_in_school = models.CharField(
+ max_length=2,
+ choices=YearInSchool.choices,
+ default=YearInSchool.FRESHMAN,
+ )
+
+ def is_upperclass(self):
+ return self.year_in_school in {YearInSchool.JUNIOR, YearInSchool.SENIOR}
+
+These work similar to :mod:`enum` from Python's standard library, but with some
+modifications:
+
+* Instead of values in the ``enum``, Django uses ``(value, label)`` tuples. The
+ ``label`` can be a lazy translatable string. If a tuple is not provided, the
+ label is automatically generated from the member name.
+* ``.label`` property is added on values, to return the label specified.
+* Number of custom properties are added to the enumeration classes --
+ ``.choices``, ``.labels``, ``.values``, and ``.names`` -- to make it easier
+ to access lists of those separate parts of the enumeration. Use ``.choices``
+ as a suitable value to pass to :attr:`~Field.choices` in a field definition.
+* The use of :func:`enum.unique()` is enforced to ensure that values cannot be
+ defined multiple times. This is unlikely to be expected in choices for a
+ field.
+
+Note that ``YearInSchool.SENIOR``, ``YearInSchool['SENIOR']``,
+``YearInSchool('SR')`` work as expected, while ``YearInSchool.SENIOR.label`` is
+a translatable string.
+
+If you don't need to have the human-readable names translated, you can have
+them inferred from the member name (replacing underscores to spaces and using
+title-case)::
+
+ class YearInSchool(models.TextChoices):
+ FRESHMAN = 'FR'
+ SOPHOMORE = 'SO'
+ JUNIOR = 'JR'
+ SENIOR = 'SR'
+ GRADUATE = 'GR'
+
+Since the case where the enum values need to be integers is extremely common,
+Django provides a ``IntegerChoices`` class. For example::
+
+ class Card(models.Model):
+
+ class Suit(models.IntegerChoices):
+ DIAMOND = 1
+ SPADE = 2
+ HEART = 3
+ CLUB = 4
+
+ suit = models.IntegerField(choices=Suit.choices)
+
+It is also possible to make use of the `Enum Functional API
+<https://docs.python.org/3/library/enum.html#functional-api>`_ with the caveat
+that labels are automatically generated as highlighted above::
+
+ >>> MedalType = models.TextChoices('MedalType', 'GOLD SILVER BRONZE')
+ >>> MedalType.choices
+ [('GOLD', 'Gold'), ('SILVER', 'Silver'), ('BRONZE', 'Bronze')]
+ >>> Place = models.IntegerChoices('Place', 'FIRST SECOND THIRD')
+ >>> Place.choices
+ [(1, 'First'), (2, 'Second'), (3, 'Third')]
+
+If you require support for a concrete data type other than ``int`` or ``str``,
+you can subclass ``Choices`` and the required concrete data type, e.g.
+:class:``datetime.date`` for use with :class:`~django.db.models.DateField`::
+
+ class MoonLandings(datetime.date, models.Choices):
+ APOLLO_11 = 1969, 7, 20, 'Apollo 11 (Eagle)'
+ APOLLO_12 = 1969, 11, 19, 'Apollo 12 (Intrepid)'
+ APOLLO_14 = 1971, 2, 5, 'Apollo 14 (Antares)'
+ APOLLO_15 = 1971, 7, 30, 'Apollo 15 (Falcon)'
+ APOLLO_16 = 1972, 4, 21, 'Apollo 16 (Orion)'
+ APOLLO_17 = 1972, 12, 11, 'Apollo 17 (Challenger)'
+
You can also collect your available choices into named groups that can
be used for organizational purposes::
@@ -148,7 +240,8 @@ The first element in each tuple is the name to apply to the group. The
second element is an iterable of 2-tuples, with each 2-tuple containing
a value and a human-readable name for an option. Grouped options may be
combined with ungrouped options within a single list (such as the
-`unknown` option in this example).
+`unknown` option in this example). Grouping is not supported by the custom
+enumeration types for managing choices.
For each model field that has :attr:`~Field.choices` set, Django will add a
method to retrieve the human-readable name for the field's current value. See
@@ -169,7 +262,19 @@ Unless :attr:`blank=False<Field.blank>` is set on the field along with a
with the select box. To override this behavior, add a tuple to ``choices``
containing ``None``; e.g. ``(None, 'Your String For Display')``.
Alternatively, you can use an empty string instead of ``None`` where this makes
-sense - such as on a :class:`~django.db.models.CharField`.
+sense - such as on a :class:`~django.db.models.CharField`. To change the label
+when using one of the custom enumeration types, set the ``__empty__`` attribute
+on the class::
+
+ class Answer(models.IntegerChoices):
+ NO = 0, _('No')
+ YES = 1, _('Yes')
+
+ __empty__ = _('(Unknown)')
+
+.. versionadded:: 3.0
+
+ The ``TextChoices``, ``IntegerChoices``, and ``Choices`` classes were added.
``db_column``
-------------