How to map Django TextChoices text to a choice?
Suppose I have this code, inspired from the Django docs about enumeration types:
class YearInSchool(models.TextChoices):
FRESHMAN = 'FR', 'Freshman'
SOPHOMORE = 'SO', 'Sophomore'
JUNIOR = 'JR', 'Junior'
SENIOR = 'SR', 'Senior'
GRADUATE = 'GR', 'Graduate'
Now suppose I have the string "Sophomore". How do I get from that to YearInSchool.SOPHOMORE
?
The only thing I can think of is a loop:
the_str = "Sophomore"
val = None
for val1, label in YearInSchool.choices:
if label == the_str:
val = YearInSchool(val1)
break
assert YearInSchool.SOPHOMORE == val
That seems awkward. Is there a better way?
You can use getattr
:
the_str = 'Sophomore'
try:
val = getattr(YearInSchool, the_str.upper())
except AttributeError:
raise AttributeError(f'{the_str.upper()} not found in {YearInSchool.names}')
assert val == YearInSchool.SOPHOMORE
I'm affraid Django doesn't implement a similar method to get_FOO_display
(described here in the doc) to achieve what you want, but I think models.TextChoices
is not intended to be used this way.
For instance, a POST request should directly contain YearInSchool.FRESHMAN.value
instead of YearInSchool.FRESHMAN.label
(at least, this is the default behavior in django.forms
), so that should not be your concern.
This being said, here is a one-liner solution which would raise a ValueError
if the_str
is not found:
val = YearInSchool.values[YearInSchool.labels.index(the_str)]
And another similar solution, twice as fast:
try:
val = next(filter(lambda x: x[1] == the_str, YearInSchool.choices))[0]
except StopIteration as err:
raise ValueError(f"{the_str} not found in {YearInSchool.labels}") from err