Python Django:如何创建类的枚举来定义选择

Python Django:如何创建类的枚举来定义选择,python,django,enums,Python,Django,Enums,如何对模型进行子类化。定义类枚举的选项 以下代码引发TypeError: from django.db.models import Choices class DataTypes(type, Choices): CHAR = str, _('Short string') INTEGER = int, _('Integer') 错误消息: dynamic_attributes = {k for c in enum_class.mro() TypeError: descripto

如何对模型进行子类化。定义类枚举的选项

以下代码引发TypeError:

from django.db.models import Choices

class DataTypes(type, Choices):
    CHAR = str, _('Short string')
    INTEGER = int, _('Integer')
错误消息:

dynamic_attributes = {k for c in enum_class.mro()
TypeError: descriptor 'mro' of 'type' object needs an argument
更新:

如果未使用mixin,则不会出现错误。但同样,成员的值没有正确转换为所需的数据类型

class DataTypes(Choices):
    CHAR = str, _('Short string')
    INTEGER = int, _('Integer')
测试:


我同意威廉·范昂森在评论中的观点。我认为不可能为类这样的数据类型使用
模型。因此,我试图找到一种能够扩展其他类的不同方法

还实现了为选项定义标签(正如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')]
str-in(DataTypes.CHAR,DataTypes.INTEGER)#True
bool in(OtherTypes.CHAR,OtherTypes.BOOLEAN)#True
DataTypes.choices#[(,'Short string'),(,'Integer')]
OtherTypes.choices#[(,'Short string'),(,'Integer'),(,'Boolean(True或False)”,(,'Floating point number')]

我同意威廉·范昂森在评论中的观点。我认为不可能为类这样的数据类型使用
模型。因此,我试图找到一种能够扩展其他类的不同方法

还实现了为选项定义标签(正如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')]
str-in(DataTypes.CHAR,DataTypes.INTEGER)#True
bool in(OtherTypes.CHAR,OtherTypes.BOOLEAN)#True
DataTypes.choices#[(,'Short string'),(,'Integer')]
OtherTypes.choices#[(,'Short string'),(,'Integer'),(,'Boolean(True或False)”,(,'Floating point number')]

您使用的是什么Django版本?我使用的是Django 3.1这是
Enum
类本身的结果:新的Enum类必须有一个基本Enum类,最多一个具体的数据类型。()。我建议只使用
选项
,因此不要键入它。对于
对象
它也不起作用。您使用的是什么Django版本?我使用的是Django 3.1这是
枚举
类本身的结果:新的枚举类必须有一个基本枚举类,最多一个具体数据类型。()。我建议只使用
选项
,因此不要键入它。对于
对象
,它也不起作用。