Best way to do enum in Sqlalchemy?

后端 未结 4 717
挽巷
挽巷 2020-12-04 11:57

I\'m reading about sqlalchemy and I saw following code:

employees_table = Table(\'employees\', metadata,
    Column(\'employee_id\', Integer, primary_key=Tru         


        
4条回答
  •  离开以前
    2020-12-04 12:29

    Note: the following is outdated. You should use sqlalchemy.types.Enum now, as recommended by Wolph. It's particularly nice as it complies with PEP-435 since SQLAlchemy 1.1.


    I like zzzeek's recipe at http://techspot.zzzeek.org/2011/01/14/the-enum-recipe/, but I changed two things:

    • I'm using the Python name of the EnumSymbol also as the name in the database, instead of using its value. I think that's less confusing. Having a separate value is still useful, e.g. for creating popup menus in the UI. The description can be considered a longer version of the value that can be used e.g. for tooltips.
    • In the original recipe, the order of the EnumSymbols is arbitrary, both when you iterate over them in Python and also when you do an "order by" on the database. But often I want to have a determinate order. So I changed the order to be alphabetic if you set the attributes as strings or tuples, or the order in which the values are declared if you explicitly set the attributes as EnumSymbols - this is using the same trick as SQLAlchemy does when it orders the Columns in DeclarativeBase classes.

    Examples:

    class EmployeeType(DeclEnum):
        # order will be alphabetic: contractor, part_time, full_time
        full_time = "Full Time"
        part_time = "Part Time"
        contractor = "Contractor"
    
    class EmployeeType(DeclEnum):
        # order will be as stated: full_time, part_time, contractor
        full_time = EnumSymbol("Full Time")
        part_time = EnumSymbol("Part Time")
        contractor = EnumSymbol("Contractor")
    

    Here is the modified recipe; it uses the OrderedDict class available in Python 2.7:

    import re
    
    from sqlalchemy.types import SchemaType, TypeDecorator, Enum
    from sqlalchemy.util import set_creation_order, OrderedDict
    
    
    class EnumSymbol(object):
        """Define a fixed symbol tied to a parent class."""
    
        def __init__(self, value, description=None):
            self.value = value
            self.description = description
            set_creation_order(self)
    
        def bind(self, cls, name):
            """Bind symbol to a parent class."""
            self.cls = cls
            self.name = name
            setattr(cls, name, self)
    
        def __reduce__(self):
            """Allow unpickling to return the symbol linked to the DeclEnum class."""
            return getattr, (self.cls, self.name)
    
        def __iter__(self):
            return iter([self.value, self.description])
    
        def __repr__(self):
            return "<%s>" % self.name
    
    
    class DeclEnumMeta(type):
        """Generate new DeclEnum classes."""
    
        def __init__(cls, classname, bases, dict_):
            reg = cls._reg = cls._reg.copy()
            for k in sorted(dict_):
                if k.startswith('__'):
                    continue
                v = dict_[k]
                if isinstance(v, basestring):
                    v = EnumSymbol(v)
                elif isinstance(v, tuple) and len(v) == 2:
                    v = EnumSymbol(*v)
                if isinstance(v, EnumSymbol):
                    v.bind(cls, k)
                    reg[k] = v
            reg.sort(key=lambda k: reg[k]._creation_order)
            return type.__init__(cls, classname, bases, dict_)
    
        def __iter__(cls):
            return iter(cls._reg.values())
    
    
    class DeclEnum(object):
        """Declarative enumeration.
    
        Attributes can be strings (used as values),
        or tuples (used as value, description) or EnumSymbols.
        If strings or tuples are used, order will be alphabetic,
        otherwise order will be as in the declaration.
    
        """
    
        __metaclass__ = DeclEnumMeta
        _reg = OrderedDict()
    
        @classmethod
        def names(cls):
            return cls._reg.keys()
    
        @classmethod
        def db_type(cls):
            return DeclEnumType(cls)
    
    
    class DeclEnumType(SchemaType, TypeDecorator):
        """DeclEnum augmented so that it can persist to the database."""
    
        def __init__(self, enum):
            self.enum = enum
            self.impl = Enum(*enum.names(), name="ck%s" % re.sub(
                '([A-Z])', lambda m: '_' + m.group(1).lower(), enum.__name__))
    
        def _set_table(self, table, column):
            self.impl._set_table(table, column)
    
        def copy(self):
            return DeclEnumType(self.enum)
    
        def process_bind_param(self, value, dialect):
            if isinstance(value, EnumSymbol):
                value = value.name
            return value
    
        def process_result_value(self, value, dialect):
            if value is not None:
                return getattr(self.enum, value.strip())
    

提交回复
热议问题