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?
EDIT: Thanks for the answers folks! I'll try them out. Just to provide more context, I am loading data from text files into a database, so the "Sophomore" is in a text file I've been provided that wasn't created by me. So, I'm stretching the use case for TextChoices, but it seemed a reasonable way to tie text file input to a DB field.
CodePudding user response:
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
CodePudding user response:
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