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
