#31261 closed New feature (needsinfo)
Add support for defining models.Choices fields with dict, tuple, set and list values.
Reported by: | Tom Forbes | Owned by: | nobody |
---|---|---|---|
Component: | Database layer (models, ORM) | Version: | dev |
Severity: | Normal | Keywords: | |
Cc: | Shai Berger, pope1ni | Triage Stage: | Unreviewed |
Has patch: | no | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description (last modified by )
As per the discussion on this forum post: https://forum.djangoproject.com/t/moving-to-django-3-0s-field-choices-enumeration-types/970/8
It would be good to support two fairly common use cases with Enums: defining groups of related values (groups), and defining grouping for use with specific widgets.
One way to enable both of these would be to simply special-case enum values with dict
, tuple
, set
and list
values:
class MoonMissions(models.IntegerChoices): APOLLO_10 = 1 APOLLO_11 = 2 EXPLORER_33 = 3 FAILED_MISSIONS = {EXPLORER_33} SUCCESSFUL_MISSIONS = (APOLLO_10, APOLLO_11) SUCCESS_GROUPING = { "Failed": FAILED_MISSIONS, "Successful": SUCCESSFUL_MISSIONS } COUNTRY_GROUPING = [ ["USA", [APOLLO_10, APOLLO_11, EXPLORER_33]] ]
Now a user can do:
SomeModel.objects.filter(mission__in=MoonMissions.FAILED_MISSIONS) enum_form_field.value in MoonMissions.SUCCESSFUL_MISSIONS models.IntegerField(choices=MoonMissions.COUNTRY_GROUPING)
Change History (9)
comment:1 by , 5 years ago
Description: | modified (diff) |
---|---|
Resolution: | → needsinfo |
Status: | new → closed |
Summary: | Add support for defining Enum fields with dict, tuple, set and list values → Add support for defining models.Choices fields with dict, tuple, set and list values. |
follow-up: 4 comment:2 by , 5 years ago
As I wrote on the forum post, we can detect if dict/list/set/tuple is in the MRO and disable this functionality in those (probably very rare) cases.
comment:3 by , 5 years ago
Yep, or we could optionally include it as a mixin. An alternative approach might be to use a nested class (like Meta
), but there are some tradeoffs there.
Perhaps this ticket should be more general: "Support for defining model choice groups in Enums"? The exact implementation might vary, but I think the overall goal is something we should work towards.
follow-up: 7 comment:4 by , 5 years ago
Cc: | added |
---|
Replying to Adam (Chainz) Johnson:
As I wrote on the forum post, we can detect if dict/list/set/tuple is in the MRO and disable this functionality in those (probably very rare) cases.
Can we? e.g. MoonLandings each option is defined by tuple but none of them is a group.
follow-up: 6 comment:5 by , 5 years ago
Perhaps this ticket should be more general: "Support for defining model choice groups in Enums"? The exact implementation might vary, but I think the overall goal is something we should work towards.
Yes. I agree with that sentiment. Grouping (both kinds) is useful. I guess the needsinfo
here just means Proof-of-concept to push is forwards.
comment:6 by , 5 years ago
Replying to Carlton Gibson:
Yes. I agree with that sentiment. Grouping (both kinds) is useful.
Yes, totally agree, I just think it's quite tricky. Maybe defining groups in __groups__ = [...]
, I'm not sure.
I guess the
needsinfo
here just means Proof-of-concept to push is forwards.
Yes, that was my intention. I would be very happy to review a patch.
follow-up: 8 comment:7 by , 5 years ago
Replying to felixxm:
Replying to Adam (Chainz) Johnson:
As I wrote on the forum post, we can detect if dict/list/set/tuple is in the MRO and disable this functionality in those (probably very rare) cases.
Can we? e.g. MoonLandings each option is defined by tuple but none of them is a group.
I'm pretty sure we can't. The way Enums work is that the value assigned to elements in the code is not an instance of the target type, but rather a set of arguments for constructing one.
Further, an important part of Choices
is the fact you can add (translatable) display strings, as a last element of a tuple.
comment:8 by , 5 years ago
Replying to Shai Berger:
Replying to felixxm:
Replying to Adam (Chainz) Johnson:
As I wrote on the forum post, we can detect if dict/list/set/tuple is in the MRO and disable this functionality in those (probably very rare) cases.
Can we? e.g. MoonLandings each option is defined by tuple but none of them is a group.
I'm pretty sure we can't.
Actually, looking again at the forum discussion, the original suggestion was to only special-case dict/list/set. This would, generally, work, all the reservations above are specifically about tuples; and further, Choices
values need to be also dictionary keys, so mutable types (like dict, set and list) are already out anyway.
comment:9 by , 5 years ago
I can construct a Choices
object with mutable values, it fails if .choices
is called on the object but otherwise it works fine.
I've found that the current code checks if the value is a list or a tuple, so something like this is currently supported:
SOME_KEY = [1, "display value]
I can't find tests for this and didn't know this was possible, but this does make it quite hard to make work as described in the ticket description without a lot of magic.
However what what about abandoning the idea of defining things using literals and use a special class:
class MoonMissions(models.IntegerChoices): ... SUCCESSFUL_MISSIONS = ChoiceSet(APOLLO_10, APOLLO_11) SUCCESS_GROUPING = DisplayGroup({ "Failed": FAILED_MISSIONS, "Successful": SUCCESSFUL_MISSIONS })
From the point of view of the metaclass this makes things a _lot_ simpler, as you don't need to try and work out if the given value is an enum value, a value with a display.
It's not as nice as using a literal but it does sidestep a lot of issues. Another thing that might be simpler to do with a class is "stitching up" the choice/group values. Ideally we'd want the values of SUCCESSFUL_MISSIONS
to be an Enum object, not the primitive type (or the tuple type if it has a display value), so the code would have to update these classes after the enum is initialized and all enum members created, replacing the raw references with their corresponding Enum objects.
Thanks for this ticket, however I'm not sure if that's feasible because we support subclasses of
Choices
for a data type other thatint
orstr
(see examples). This can be break backward compatibility. I would be very happy to evaluate a patch, but without breaking a current behavior.