I want to limit the input to certain choices so I create a list/tuple of
tuples/lists. Since Python 3.4 there is a class called
[https://docs.python.org/3.5/library/enum.html Enum] and some nice
decorators like [https://docs.python.org/3.5/library/enum.html#ensuring-
unique-enumeration-values @unique].
Currently, if you want to use this you have to do 1/2 dirty hacks,
descriped in [http://blog.richard.do/index.php/2014/02/how-to-use-enums-
for-django-field-choices/ this article], which I would prefer over the
current solution.
It would be nice to have native support for the Enum class, e.g. you can
directly pass the class to choices (instead of using the class method
choices() ), also when refering to an element in the enum just using
{{{Student.Freshmann}}} instead of {{{Student.Freshmann.value}}}
A simple example would be this:
{{{
from enum import Enum
class Student(models.Model):
class YearInSchoolChoices(Enum):
Freshman = 'FR'
Sophomore = 'SO'
Junior = 'JR'
Senior = 'SR'
year_in_school = models.CharField(
max_length=2,
choices=YearInSchoolChoices,
default=YearInSchoolChoices.Freshman,
)
def is_upperclass(self):
return self.year_in_school in (self.YearInSchoolChoices.Junior,
self.YearInSchoolChoices.Senior)
}}}
Also this could be adopted (if accepted) to any type of choices in django,
e.g. [https://docs.djangoproject.com/en/1.10/ref/forms/fields/#choicefield
Choicefield] etc.
--
Ticket URL: <https://code.djangoproject.com/ticket/27910>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
Comment (by Simon Charette):
Do you have a suggestion about how choices translations should be defined?
--
Ticket URL: <https://code.djangoproject.com/ticket/27910#comment:1>
Comment (by Marcel Hellwig):
To be honest, I haven't though about this, because I don't translate them.
Hmm.. Good question. Implicit translation?! :/ Decorator? That are not so
good ideas.
Or you could swap the positions, e.g. {{{ FR = _("Freshman") }}}, because
you'll never translate the key which is stored in the DB, right?!
--
Ticket URL: <https://code.djangoproject.com/ticket/27910#comment:2>
Comment (by Simon Charette):
> Or you could swap the positions.
That would work for text based fields (`CharField`, `TextField`) but what
about choices defined for a integer based field?
I feel like ''choices'' are a different concept than enums and that while
the later is useful for referring to choices values as constants, choices
and label mapping should be defined independently.
{{{#!python
class StudentType(enum.Enum):
Freshman = 'FR'
Sophomore = 'SO'
Junior = 'JR'
Senior = 'SR'
class Student(models.Model):
type = models.CharField(choices=[
(StudentType.Freshman, _('Freshman')),
(StudentType.Sophomore, _('Sophomore')),
(StudentType.Junior, _('Junior')),
(StudentType.Senior, _('Senior')),
])
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/27910#comment:3>
Comment (by Marcel Hellwig):
Hmm.. your solution won't bring any advantages over the current one, so..
I don't like that concept. I think the first one was okay, but we will
have Problems when you want to use translation or any non-variable-names.
So.. should be discard this idea (although I really do like it!)
--
Ticket URL: <https://code.djangoproject.com/ticket/27910#comment:4>
* status: new => closed
* resolution: => wontfix
Comment:
I agree with Simon's analysis. An automatic translation based on the enum
value doesn't seem like a proper separate of concerns.
--
Ticket URL: <https://code.djangoproject.com/ticket/27910#comment:5>
Comment (by Tom):
I know this is a closed ticket, but a pattern I've has success with is a
custom `enum` subclass that combines the values with the display:
{{{#!python
class StudentType(EnumChoices):
Freshman = 'FR', _('Freshman')
Sophomore = 'SO', _('Sophomore')
Junior = 'JR', _('Junior')
Senior = 'SR', _('Senior')
}}}
It's somewhat simple to do, the EnumChoices class splits the initial enum
values into value, display, and exposes the display text as an attribute:
`StudentType.Freshman.display` (and in the __str__ method).
list(StudentType) returns a list of tuples that is suitable for `choices`.
Could this not be an OK compromise? In the applications I've seen with
large numbers of choices managing the display values gets to be a burden,
the values should indeed be separate from the display and not auto-
generated but there is a tangible benefit in keeping their definitions as
close as possible together. Splitting the definition of an enum out into
two parts in the django-way seems to violate DRY, no?
--
Ticket URL: <https://code.djangoproject.com/ticket/27910#comment:6>
* cc: Tom (added)
--
Ticket URL: <https://code.djangoproject.com/ticket/27910#comment:7>
Comment (by Marcel Hellwig):
I like the solution Tim brought up. Maybe making the second item on the
enum optional, so that you could still write
{{{
Freshman = 'FR',
}}}
and for default just use an untranslated name of the field (e.g.
Freshman).
How do you like it?
--
Ticket URL: <https://code.djangoproject.com/ticket/27910#comment:8>
* cc: Ryan Hiebert (added)
--
Ticket URL: <https://code.djangoproject.com/ticket/27910#comment:9>
* status: closed => new
* resolution: wontfix =>
Comment:
Reopening, following the [https://groups.google.com/forum/#!msg/django-
developers/bCJ28uz0QFE/UZVIMWtWDgAJ discussion on the developers list] and
oral discussions with technical-team members on the DC-EU sprints.
--
Ticket URL: <https://code.djangoproject.com/ticket/27910#comment:10>
* owner: nobody => Shai Berger
* status: new => assigned
* version: 1.10 => master
Comment:
[https://github.com/django/django/pull/11223 PR]
--
Ticket URL: <https://code.djangoproject.com/ticket/27910#comment:11>
* stage: Unreviewed => Accepted
Comment:
Given the extended discussion on django-developers to re-open this ticket
I'll move it to "accepted": (group) review will decide finally if we're
going to merge. (But if I read the thread right, the consensus seemed to
be in favour...)
--
Ticket URL: <https://code.djangoproject.com/ticket/27910#comment:12>
* has_patch: 0 => 1
--
Ticket URL: <https://code.djangoproject.com/ticket/27910#comment:13>
Comment (by Luke Plant):
This ticket really ought to consider the use of database level enums for
the field. (e.g. https://www.postgresql.org/docs/9.1/datatype-enum.html ).
They provide a lot of efficiency (storage and searching), while allowing
the DB to contain human readable values (from the point of view of anyone
querying it), and also having tight data integrity - you get the best of
everything. MySQL also has support, but it looks a bit different.
[https://github.com/ashleywaite/django-more/tree/master/django_enum
django_enum] implements this, but it requires a significant number of
patches to Django machinery, which django_enum does by monkey patching. In
particular, you need to track changes to the enum in order to generate
'CREATE TYPE' and 'ALTER TYPE' (for Postgres at least). It would be great
to have a solution in Django core so that we could avoid that kind of
monkey patching.
We might want to consider this as a separate ticket, but it would be a
shame to solve the problem half-way. At the very least we should ensure
that the solution we adopt would be future compatible with using DB-level
enums.
There may well be some good ideas from
[https://techspot.zzzeek.org/2011/01/14/the-enum-recipe/ Mike Bayer's post
about enums] that we can make use of. His `DeclEnum` base class is very
similar in interface to `EnumChoices`, which is a good sign at least!
--
Ticket URL: <https://code.djangoproject.com/ticket/27910#comment:14>
Comment (by Simon Charette):
> This ticket really ought to consider the use of database level enums for
the field. (e.g. https://www.postgresql.org/docs/9.1/datatype-enum.html
). They provide a lot of efficiency (storage and searching), while
allowing the DB to contain human readable values (from the point of view
of anyone querying it), and also having tight data integrity - you get the
best of everything. MySQL also has support, but it looks a bit different.
Just wanted to drop a note to mention that PostgreSQL's enum type doesn't
support value removal (as of 11) and that MySQL stores enum as tinyint
index of the values so if you perform an ordering change you pretty much
corrupt your column. The index based value on MySQL poses notable problems
when joining similar values across tables
[https://dev.mysql.com/doc/refman/5.7/en/enum.html or ordering by such a
column]. I believe these two reasons make uses database level enums in a
database agnostic way really challenging.
--
Ticket URL: <https://code.djangoproject.com/ticket/27910#comment:15>
* needs_docs: 0 => 1
--
Ticket URL: <https://code.djangoproject.com/ticket/27910#comment:16>
* owner: Shai Berger => Nick Pope
Comment:
New [https://github.com/django/django/pull/11540 PR]
--
Ticket URL: <https://code.djangoproject.com/ticket/27910#comment:17>
* needs_docs: 1 => 0
--
Ticket URL: <https://code.djangoproject.com/ticket/27910#comment:18>
* status: assigned => closed
* resolution: => fixed
Comment:
In [changeset:"72ebe85a269aab4bdb3829de4846b41f90973c5d" 72ebe85]:
{{{
#!CommitTicketReference repository=""
revision="72ebe85a269aab4bdb3829de4846b41f90973c5d"
Fixed #27910 -- Added enumeration helpers for use in Field.choices.
These classes can serve as a base class for user enums, supporting
translatable human-readable names, or names automatically inferred
from the enum member name.
Additional properties make it easy to access the list of names, values
and display labels.
Thanks to the following for ideas and reviews:
Carlton Gibson, Fran Hrženjak, Ian Foote, Mariusz Felisiak, Shai Berger.
Co-authored-by: Shai Berger <sh...@platonix.com>
Co-authored-by: Nick Pope <nick...@flightdataservices.com>
Co-authored-by: Mariusz Felisiak <felisiak...@gmail.com>
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/27910#comment:19>
Comment (by Mariusz Felisiak <felisiak.mariusz@…>):
In [changeset:"1c66767d4e472fea27798812817b8a47c6ae22b3" 1c66767d]:
{{{
#!CommitTicketReference repository=""
revision="1c66767d4e472fea27798812817b8a47c6ae22b3"
Refs #27910 -- Improved documentation for model field choice enumeration
types.
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/27910#comment:20>
Comment (by Mariusz Felisiak <felisiak.mariusz@…>):
In [changeset:"4d72c14bafefddd073b715c59834494ab77e9aa1" 4d72c14b]:
{{{
#!CommitTicketReference repository=""
revision="4d72c14bafefddd073b715c59834494ab77e9aa1"
[3.0.x] Refs #27910 -- Improved documentation for model field choice
enumeration types.
Backport of 1c66767d4e472fea27798812817b8a47c6ae22b3 from master
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/27910#comment:21>
Comment (by Mariusz Felisiak <felisiak.mariusz@…>):
In [changeset:"aad46ee274b0e294ac055cc199e6595de4ef4164" aad46ee2]:
{{{
#!CommitTicketReference repository=""
revision="aad46ee274b0e294ac055cc199e6595de4ef4164"
Refs #27910 -- Doc'd support for enumeration types serialization in
migrations.
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/27910#comment:22>
Comment (by Mariusz Felisiak <felisiak.mariusz@…>):
In [changeset:"fcaaff9862a93efe8ccd2a23b7e38d62dcf7a9d2" fcaaff9]:
{{{
#!CommitTicketReference repository=""
revision="fcaaff9862a93efe8ccd2a23b7e38d62dcf7a9d2"
[3.0.x] Refs #27910 -- Doc'd support for enumeration types serialization
in migrations.
Backport of aad46ee274b0e294ac055cc199e6595de4ef4164 from master
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/27910#comment:23>
Comment (by GitHub <noreply@…>):
In [changeset:"1877ec18753947795b7821f8b9176bd7ea7c03a3" 1877ec1]:
{{{
#!CommitTicketReference repository=""
revision="1877ec18753947795b7821f8b9176bd7ea7c03a3"
Refs #27910 -- Added __init__.py file for model_enums tests.
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/27910#comment:24>
Comment (by Mariusz Felisiak <felisiak.mariusz@…>):
In [changeset:"74c5ddc657ef23463793fc466282153d559e67a4" 74c5ddc6]:
{{{
#!CommitTicketReference repository=""
revision="74c5ddc657ef23463793fc466282153d559e67a4"
[3.0.x] Refs #27910 -- Added __init__.py file for model_enums tests.
Backport of 1877ec18753947795b7821f8b9176bd7ea7c03a3 from master
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/27910#comment:25>