Как сопоставить текст Django TextChoices с выбором?

Предположим, у меня есть такой код, вдохновленный документацией Django о типах перечислений:

class YearInSchool(models.TextChoices):
        FRESHMAN = 'FR', 'Freshman'
        SOPHOMORE = 'SO', 'Sophomore'
        JUNIOR = 'JR', 'Junior'
        SENIOR = 'SR', 'Senior'
        GRADUATE = 'GR', 'Graduate'

Теперь предположим, что у меня есть строка "Sophomore". Как мне перейти от нее к YearInSchool.SOPHOMORE?

Единственное, что я могу придумать, это цикл:

the_str = "Sophomore"
val = None
for val1, label in YearInSchool.choices:
    if label == the_str:
        val = YearInSchool(val1)
        break

assert YearInSchool.SOPHOMORE == val

Это кажется неудобным. Есть ли лучший способ?

Вы можете использовать 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

Боюсь, что в Django не реализован метод, подобный get_FOO_display (описанный здесь в doc) для достижения того, что вы хотите, но я думаю, что models.TextChoices не предназначен для использования таким образом.

Например, POST-запрос должен напрямую содержать YearInSchool.FRESHMAN.value вместо YearInSchool.FRESHMAN.label (по крайней мере, это поведение по умолчанию в django.forms), так что это не должно вас беспокоить.

Согласно этому, вот однострочное решение, которое вызовет сообщение ValueError, если the_str не найдено:

val = YearInSchool.values[YearInSchool.labels.index(the_str)]

И еще одно аналогичное решение, в два раза быстрее:

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
Вернуться на верх