我同意Willem Van Onsem 在cmets 中的观点。我认为不可能将models.Choices 用于类等数据类型。所以我试图找到一种能够扩展其他类的不同方法。
还实现了为选择定义标签(如 Django Choices 所做的那样)。
def _is_dunder(name):
"""
Stolen liberally from 'aenum'.
Returns True if a __dunder__ name, False otherwise.
"""
return (len(name) > 4 and
name[:2] == name[-2:] == '__' and
name[2] != '_' and
name[-3] != '_')
class ChoicesMeta(type):
def __new__(mcs, cls, bases, attrs):
# ignore any keys listed in _ignore_
ignore = attrs.setdefault('_ignore_', [])
ignore.append('_ignore_')
# save constant names into list.
names = [k for k in attrs if not(_is_dunder(k) or k in ignore)]
# save constant labels into list.
labels = []
for k in names:
value = attrs[k]
if (
isinstance(value, (list, tuple)) and
len(value) > 1 and
isinstance(value[-1], (Promise, str))
):
value, label = value
else:
label = k.replace('_', ' ').title()
labels.append(label)
attrs[k] = value
new_cls = super().__new__(mcs, cls, bases, attrs)
new_cls.local_names = names
new_cls.local_labels = labels
return new_cls
@property
def names(cls):
names = []
for c in reversed(cls.__mro__):
names.extend(getattr(c, 'local_names', []))
return list(dict.fromkeys(names))
@property
def choices(cls):
empty = [(None, cls.__empty__)] if hasattr(cls, '__empty__') else []
return empty + [(getattr(cls, name), cls.labels[i]) for i, name in enumerate(cls.names)]
@property
def labels(cls):
labels = []
for c in reversed(cls.__mro__):
labels.extend(getattr(c, 'local_labels', []))
return list(dict.fromkeys(labels))
class Choices(metaclass=ChoicesMeta):
pass
现在您可以创建自己的 Choices 类并对其进行子类化:
class DataTypes(Choices):
CHAR = str, _('Short string')
INTEGER = int
class OtherTypes(DataTypes):
BOOLEAN = bool, _('Boolean (Either True or False)')
FLOAT = float, _('Floating point number')
测试:
str in (DataTypes.CHAR, DataTypes.INTEGER) # True
bool in (OtherTypes.CHAR, OtherTypes.BOOLEAN) # True
DataTypes.choices # [(<class 'str'>, 'Short string'), (<class 'int'>, 'Integer')]
OtherTypes.choices # [(<class 'str'>, 'Short string'), (<class 'int'>, 'Integer'), (<class 'bool'>, 'Boolean (Either True or False)'), (<class 'float'>, 'Floating point number')]