Как в Django определить строковое значение для перечисления IntegerChoices?
Я использую Django 3.2 и Pythnon 3.9. В моей модели я определяю перечисление int. Я хотел бы также определить для него читаемые строковые значения, поэтому я попробовал
class Transaction(models.Model):
class TransactionTypes(models.IntegerChoices):
BUY = 0
SELL = 1
labels = {
BUY: 'Buy',
SELL: 'Sell'
}
translation = {v: k for k, v in labels.items()}
но это определение не работает с ошибкой
TypeError: int() argument must be a string, a bytes-like object or a number, not 'dict'
Как мне определить строки для каждого значения? Я не возражаю, если эти строки будут просто буквальными именами переменных (например, "BUY", "SELL")
Один из способов заключается в том, что вы определяете варианты как кортеж кортежей, каждый вариант является значением внешнего кортежа. Первый элемент в каждом внутреннем кортеже - это значение, которое должно быть установлено в модели, а второй элемент - его строковое представление. как в приведенном ниже фрагменте кода:
class Transaction(models.Model):
BUY = 0
SELL = 1
TRANSACTION_TYPE_CHOICES = (
(BUY, 'Buy'),
(SELL, 'Sell'),
)
type_of_transaction = models.IntegerField(
max_length=4,
choices=TRANSACTION_TYPE_CHOICES,
default=BUY,
)
Я ценю последовательное определение, но есть и другой ответ на этот вопрос - использование перечислений, и я думаю, что тип Enum, безусловно, лучший. Они могут одновременно отображать целое число и строку для элемента, при этом ваш код остается более читабельным.См. фрагменты кода ниже:
app/enums.py
from enum import Enum
class ChoiceEnum(Enum):
def __str__(self):
return self.name
def __int__(self):
return self.value
@classmethod
def choices(cls):
choices = list()
for item in cls: # Loop thru defined enums
choices.append((item.value, item.name))
return tuple(choices)
class TransactionType(ChoiceEnum):
BUY = 0
SELL = 1
# Uh oh
TransactionType.BUY._name_ = 'Buy'
TransactionType.SELL._name_ = 'Sell'
app/models.py
from django.db import models
from myapp.enums import TransactionType
class Transaction(models.Model):
type_of_transaction = models.IntegerField(choices=TransactionType.choices(), default=int(TransactionType.BUY))
# ...
Более простой способ, согласно официальной документации для Django3.2
class Transaction(models.Model):
class TransactionTypes(models.IntegerChoices):
BUY = 0, _('Buy')
SELL = 1, _('Sell')
(или)
class Transaction(models.Model):
class TransactionTypes(models.IntegerChoices):
BUY = 0, 'Buy'
SELL = 1, 'Sell'
Другой способ - использовать Enum functional api, это также упоминается в официальной документации Django 3.2
TransactionTypes = models.IntegerChoices('TransactionTypes', 'BUY SELL')
TransactionTypes.choices
#provides below output
>>>[(1, 'Buy'), (2, 'Sell')]
Просто создайте варианты выбора, как указано в Django Choices,
class TransactionTypes(models.IntegerChoices):
BUY = 0, 'Buy'
SELL = 1, 'Sell'
Итак, эффективно ваша модель будет выглядеть так,
class Transaction(models.Model):
class TransactionTypes(models.IntegerChoices):
BUY = 0, 'Buy'
SELL = 1, 'Sell'
type = models.PositiveSmallIntegerField(choices=TransactionTypes.choices)
def __str__(self):
return str(self.type)
Я увидел, что у вас есть значение "string" из поля .type
. Я твердо уверен, что этого не произойдет, так как это поле integer в БД и, конечно, либо БД выдаст ошибку, либо приведет значение к целому числу, когда вы попытаетесь сохранить поле со строковым значением.
In [12]: txn_1 = Transaction.objects.create(type=Transaction.TransactionTypes.SELL)
In [13]: txn_1.refresh_from_db()
In [14]: type(txn_1.type)
Out[14]: int
In [15]: txn_2 = Transaction.objects.create(type="1")
In [16]: txn_2.refresh_from_db()
In [17]: type(txn_2.type)
Out[17]: int