mirror of https://github.com/django/django.git
[3.0.x] Refs #27910 -- Improved documentation for model field choice enumeration types.
Backport of 1c66767d4e
from master
This commit is contained in:
parent
1fa629cd8f
commit
4d72c14baf
|
@ -130,6 +130,59 @@ model class keeps all of that information with the class that uses it,
|
||||||
and helps reference the choices (e.g, ``Student.SOPHOMORE``
|
and helps reference the choices (e.g, ``Student.SOPHOMORE``
|
||||||
will work anywhere that the ``Student`` model has been imported).
|
will work anywhere that the ``Student`` model has been imported).
|
||||||
|
|
||||||
|
.. _field-choices-named-groups:
|
||||||
|
|
||||||
|
You can also collect your available choices into named groups that can
|
||||||
|
be used for organizational purposes::
|
||||||
|
|
||||||
|
MEDIA_CHOICES = [
|
||||||
|
('Audio', (
|
||||||
|
('vinyl', 'Vinyl'),
|
||||||
|
('cd', 'CD'),
|
||||||
|
)
|
||||||
|
),
|
||||||
|
('Video', (
|
||||||
|
('vhs', 'VHS Tape'),
|
||||||
|
('dvd', 'DVD'),
|
||||||
|
)
|
||||||
|
),
|
||||||
|
('unknown', 'Unknown'),
|
||||||
|
]
|
||||||
|
|
||||||
|
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).
|
||||||
|
|
||||||
|
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
|
||||||
|
:meth:`~django.db.models.Model.get_FOO_display` in the database API
|
||||||
|
documentation.
|
||||||
|
|
||||||
|
Note that choices can be any sequence object -- not necessarily a list or
|
||||||
|
tuple. This lets you construct choices dynamically. But if you find yourself
|
||||||
|
hacking :attr:`~Field.choices` to be dynamic, you're probably better off using
|
||||||
|
a proper database table with a :class:`ForeignKey`. :attr:`~Field.choices` is
|
||||||
|
meant for static data that doesn't change much, if ever.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
A new migration is created each time the order of ``choices`` changes.
|
||||||
|
|
||||||
|
.. _field-choices-blank-label:
|
||||||
|
|
||||||
|
Unless :attr:`blank=False<Field.blank>` is set on the field along with a
|
||||||
|
:attr:`~Field.default` then a label containing ``"---------"`` will be rendered
|
||||||
|
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`.
|
||||||
|
|
||||||
|
.. _field-choices-enum-types:
|
||||||
|
|
||||||
|
Enumeration types
|
||||||
|
~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
In addition, Django provides enumeration types that you can subclass to define
|
In addition, Django provides enumeration types that you can subclass to define
|
||||||
choices in a concise way::
|
choices in a concise way::
|
||||||
|
|
||||||
|
@ -156,11 +209,17 @@ choices in a concise way::
|
||||||
These work similar to :mod:`enum` from Python's standard library, but with some
|
These work similar to :mod:`enum` from Python's standard library, but with some
|
||||||
modifications:
|
modifications:
|
||||||
|
|
||||||
* Instead of values in the ``enum``, Django uses ``(value, label)`` tuples. The
|
* Enum member values are a tuple of arguments to use when constructing the
|
||||||
``label`` can be a lazy translatable string. If a tuple is not provided, the
|
concrete data type. Django supports adding an extra string value to the end
|
||||||
label is automatically generated from the member name.
|
of this tuple to be used as the human-readable name, or ``label``. The
|
||||||
* ``.label`` property is added on values, to return the label specified.
|
``label`` can be a lazy translatable string. Thus, in most cases, the member
|
||||||
* Number of custom properties are added to the enumeration classes --
|
value will be a ``(value, label)`` two-tuple. See below for :ref:`an example
|
||||||
|
of subclassing choices <field-choices-enum-subclassing>` using a more complex
|
||||||
|
data type. If a tuple is not provided, or the last item is not a (lazy)
|
||||||
|
string, the ``label`` is :ref:`automatically generated
|
||||||
|
<field-choices-enum-auto-label>` from the member name.
|
||||||
|
* A ``.label`` property is added on values, to return the human-readable name.
|
||||||
|
* A number of custom properties are added to the enumeration classes --
|
||||||
``.choices``, ``.labels``, ``.values``, and ``.names`` -- to make it easier
|
``.choices``, ``.labels``, ``.values``, and ``.names`` -- to make it easier
|
||||||
to access lists of those separate parts of the enumeration. Use ``.choices``
|
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.
|
as a suitable value to pass to :attr:`~Field.choices` in a field definition.
|
||||||
|
@ -168,23 +227,26 @@ modifications:
|
||||||
defined multiple times. This is unlikely to be expected in choices for a
|
defined multiple times. This is unlikely to be expected in choices for a
|
||||||
field.
|
field.
|
||||||
|
|
||||||
Note that ``YearInSchool.SENIOR``, ``YearInSchool['SENIOR']``,
|
Note that using ``YearInSchool.SENIOR``, ``YearInSchool['SENIOR']``, or
|
||||||
``YearInSchool('SR')`` work as expected, while ``YearInSchool.SENIOR.label`` is
|
``YearInSchool('SR')`` to access or lookup enum members work as expected, as do
|
||||||
a translatable string.
|
the ``.name`` and ``.value`` properties on the members.
|
||||||
|
|
||||||
|
.. _field-choices-enum-auto-label:
|
||||||
|
|
||||||
If you don't need to have the human-readable names translated, you can have
|
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
|
them inferred from the member name (replacing underscores with spaces and using
|
||||||
title-case)::
|
title-case)::
|
||||||
|
|
||||||
class YearInSchool(models.TextChoices):
|
>>> class Vehicle(models.TextChoices):
|
||||||
FRESHMAN = 'FR'
|
... CAR = 'C'
|
||||||
SOPHOMORE = 'SO'
|
... TRUCK = 'T'
|
||||||
JUNIOR = 'JR'
|
... JET_SKI = 'J'
|
||||||
SENIOR = 'SR'
|
...
|
||||||
GRADUATE = 'GR'
|
>>> Vehicle.JET_SKI.label
|
||||||
|
'Jet Ski'
|
||||||
|
|
||||||
Since the case where the enum values need to be integers is extremely common,
|
Since the case where the enum values need to be integers is extremely common,
|
||||||
Django provides a ``IntegerChoices`` class. For example::
|
Django provides an ``IntegerChoices`` class. For example::
|
||||||
|
|
||||||
class Card(models.Model):
|
class Card(models.Model):
|
||||||
|
|
||||||
|
@ -207,9 +269,11 @@ that labels are automatically generated as highlighted above::
|
||||||
>>> Place.choices
|
>>> Place.choices
|
||||||
[(1, 'First'), (2, 'Second'), (3, 'Third')]
|
[(1, 'First'), (2, 'Second'), (3, 'Third')]
|
||||||
|
|
||||||
|
.. _field-choices-enum-subclassing:
|
||||||
|
|
||||||
If you require support for a concrete data type other than ``int`` or ``str``,
|
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.
|
you can subclass ``Choices`` and the required concrete data type, e.g.
|
||||||
:class:``datetime.date`` for use with :class:`~django.db.models.DateField`::
|
:class:`~datetime.date` for use with :class:`~django.db.models.DateField`::
|
||||||
|
|
||||||
class MoonLandings(datetime.date, models.Choices):
|
class MoonLandings(datetime.date, models.Choices):
|
||||||
APOLLO_11 = 1969, 7, 20, 'Apollo 11 (Eagle)'
|
APOLLO_11 = 1969, 7, 20, 'Apollo 11 (Eagle)'
|
||||||
|
@ -219,52 +283,14 @@ you can subclass ``Choices`` and the required concrete data type, e.g.
|
||||||
APOLLO_16 = 1972, 4, 21, 'Apollo 16 (Orion)'
|
APOLLO_16 = 1972, 4, 21, 'Apollo 16 (Orion)'
|
||||||
APOLLO_17 = 1972, 12, 11, 'Apollo 17 (Challenger)'
|
APOLLO_17 = 1972, 12, 11, 'Apollo 17 (Challenger)'
|
||||||
|
|
||||||
You can also collect your available choices into named groups that can
|
There are some additional caveats to be aware of:
|
||||||
be used for organizational purposes::
|
|
||||||
|
|
||||||
MEDIA_CHOICES = [
|
- Enumeration types do not support :ref:`named groups
|
||||||
('Audio', (
|
<field-choices-named-groups>`.
|
||||||
('vinyl', 'Vinyl'),
|
- Because an enumeration with a concrete data type requires all values to match
|
||||||
('cd', 'CD'),
|
the type, overriding the :ref:`blank label <field-choices-blank-label>`
|
||||||
)
|
cannot be achieved by creating a member with a value of ``None``. Instead,
|
||||||
),
|
set the ``__empty__`` attribute on the class::
|
||||||
('Video', (
|
|
||||||
('vhs', 'VHS Tape'),
|
|
||||||
('dvd', 'DVD'),
|
|
||||||
)
|
|
||||||
),
|
|
||||||
('unknown', 'Unknown'),
|
|
||||||
]
|
|
||||||
|
|
||||||
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). 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
|
|
||||||
:meth:`~django.db.models.Model.get_FOO_display` in the database API
|
|
||||||
documentation.
|
|
||||||
|
|
||||||
Note that choices can be any sequence object -- not necessarily a list or
|
|
||||||
tuple. This lets you construct choices dynamically. But if you find yourself
|
|
||||||
hacking :attr:`~Field.choices` to be dynamic, you're probably better off using
|
|
||||||
a proper database table with a :class:`ForeignKey`. :attr:`~Field.choices` is
|
|
||||||
meant for static data that doesn't change much, if ever.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
A new migration is created each time the order of ``choices`` changes.
|
|
||||||
|
|
||||||
Unless :attr:`blank=False<Field.blank>` is set on the field along with a
|
|
||||||
:attr:`~Field.default` then a label containing ``"---------"`` will be rendered
|
|
||||||
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`. To change the label
|
|
||||||
when using one of the custom enumeration types, set the ``__empty__`` attribute
|
|
||||||
on the class::
|
|
||||||
|
|
||||||
class Answer(models.IntegerChoices):
|
class Answer(models.IntegerChoices):
|
||||||
NO = 0, _('No')
|
NO = 0, _('No')
|
||||||
|
|
|
@ -90,7 +90,7 @@ and ``IntegerChoices`` types are provided for text and integer fields. The
|
||||||
``Choices`` class allows defining a compatible enumeration for other concrete
|
``Choices`` class allows defining a compatible enumeration for other concrete
|
||||||
data types. These custom enumeration types support human-readable labels that
|
data types. These custom enumeration types support human-readable labels that
|
||||||
can be translated and accessed via a property on the enumeration or its
|
can be translated and accessed via a property on the enumeration or its
|
||||||
members. See :ref:`Field.choices documentation <field-choices>` for more
|
members. See :ref:`Enumeration types <field-choices-enum-types>` for more
|
||||||
details and examples.
|
details and examples.
|
||||||
|
|
||||||
Minor features
|
Minor features
|
||||||
|
|
Loading…
Reference in New Issue