Beste Weg, um enum in Sqlalchemy?

Ich lese sqlalchemy und ich sehe den Code

employees_table = Table('employees', metadata, Column('employee_id', Integer, primary_key=True), Column('name', String(50)), Column('manager_data', String(50)), Column('engineer_info', String(50)), Column('type', String(20), nullable=False) ) employee_mapper = mapper(Employee, employees_table, \ polymorphic_on=employees_table.c.type, polymorphic_identity='employee') manager_mapper = mapper(Manager, inherits=employee_mapper, polymorphic_identity='manager') engineer_mapper = mapper(Engineer, inherits=employee_mapper, polymorphic_identity='engineer') 

Soll ich "Typ" ein int machen, mit Konstanten in einer Bibliothek? Oder soll ich nur machen Typ ein enum?

4 Solutions collect form web for “Beste Weg, um enum in Sqlalchemy?”

SQLAlchemy hat einen Enum-Typ seit 0.6: http://docs.sqlalchemy.org/en/latest/core/type_basics.html?highlight=enum#sqlalchemy.types.Enum

Obwohl ich nur empfehlen würde es die Verwendung, wenn Ihre Datenbank hat eine native enum Typ. Ansonsten würde ich persönlich nur ein int verwenden.

Pythons Aufzählungsarten sind direkt vom SQLAlchemy-Enum-Typ ab SQLAlchemy 1.1 akzeptabel:

 import enum class MyEnum(enum.Enum): one = 1 two = 2 three = 3 class MyClass(Base): __tablename__ = 'some_table' id = Column(Integer, primary_key=True) value = Column(Enum(MyEnum)) 

Beachten Sie, dass oben die Zeichenfolge "Eins", "Zwei", "Drei" beibehalten werden, nicht die Integer-Werte.

Für ältere Versionen von SQLAlchemy schrieb ich einen Beitrag, der seinen eigenen Enumerated-Typ ( http://techspot.zzzeek.org/2011/01/14/the-enum-recipe/ )

 from sqlalchemy.types import SchemaType, TypeDecorator, Enum from sqlalchemy import __version__ import re if __version__ < '0.6.5': raise NotImplementedError("Version 0.6.5 or higher of SQLAlchemy is required.") class EnumSymbol(object): """Define a fixed symbol tied to a parent class.""" def __init__(self, cls_, name, value, description): self.cls_ = cls_ self.name = name self.value = value self.description = description 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 EnumMeta(type): """Generate new DeclEnum classes.""" def __init__(cls, classname, bases, dict_): cls._reg = reg = cls._reg.copy() for k, v in dict_.items(): if isinstance(v, tuple): sym = reg[v[0]] = EnumSymbol(cls, k, *v) setattr(cls, k, sym) return type.__init__(cls, classname, bases, dict_) def __iter__(cls): return iter(cls._reg.values()) class DeclEnum(object): """Declarative enumeration.""" __metaclass__ = EnumMeta _reg = {} @classmethod def from_string(cls, value): try: return cls._reg[value] except KeyError: raise ValueError( "Invalid value for %r: %r" % (cls.__name__, value) ) @classmethod def values(cls): return cls._reg.keys() @classmethod def db_type(cls): return DeclEnumType(cls) class DeclEnumType(SchemaType, TypeDecorator): def __init__(self, enum): self.enum = enum self.impl = Enum( *enum.values(), name="ck%s" % re.sub( '([AZ])', 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 value is None: return None return value.value def process_result_value(self, value, dialect): if value is None: return None return self.enum.from_string(value.strip()) if __name__ == '__main__': from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column, Integer, String, create_engine from sqlalchemy.orm import Session Base = declarative_base() class EmployeeType(DeclEnum): part_time = "P", "Part Time" full_time = "F", "Full Time" contractor = "C", "Contractor" class Employee(Base): __tablename__ = 'employee' id = Column(Integer, primary_key=True) name = Column(String(60), nullable=False) type = Column(EmployeeType.db_type()) def __repr__(self): return "Employee(%r, %r)" % (self.name, self.type) e = create_engine('sqlite://', echo=True) Base.metadata.create_all(e) sess = Session(e) sess.add_all([ Employee(name='e1', type=EmployeeType.full_time), Employee(name='e2', type=EmployeeType.full_time), Employee(name='e3', type=EmployeeType.part_time), Employee(name='e4', type=EmployeeType.contractor), Employee(name='e5', type=EmployeeType.contractor), ]) sess.commit() print sess.query(Employee).filter_by(type=EmployeeType.contractor).all() 

Ich bin in SQLAlchemy nicht wirklich sachkundig, aber dieser Ansatz von Paulo schien mir viel einfacher zu sein.
Ich brauche keine benutzerfreundlichen Beschreibungen, also ging ich damit hin.

Zitat von Paulo (ich hoffe, dass es mir nichts ausmacht, dass ich es hier umkomme):

Python's namedtuple Sammlung zur Rettung. Wie der Name schon sagt, ist ein namedtuple ein Tupel mit jedem Element mit einem Namen. Wie ein gewöhnliches Tupel sind die Gegenstände unveränderlich. Im Gegensatz zu einem gewöhnlichen Tupel kann der Wert eines Elements über seinen Namen mit der Punktnotation aufgerufen werden.

Hier ist eine Utility-Funktion zum Erstellen eines namedtuple :

 from collections import namedtuple def create_named_tuple(*values): return namedtuple('NamedTuple', values)(*values) 

Die * vor der Wertevariable ist für das "Auspacken" der Elemente der Liste, so dass jedes Element als ein einzelnes Argument an die Funktion übergeben wird.

Um ein namedtuple zu erstellen, namedtuple einfach die obige Funktion mit den benötigten Werten auf:

 >>> project_version = create_named_tuple('alpha', 'beta', 'prod') NamedTuple(alpha='alpha', beta='beta', prod='prod') 

Wir können nun die Funktion project_version namedtuple verwenden, um die Werte des Versionsfeldes anzugeben.

 class Project(Base): ... version = Column(Enum(*project_version._asdict().values(), name='projects_version')) ... 

Das ist gut für mich und ist so viel einfacher als die anderen Lösungen, die ich vorher gefunden habe.

Hinweis: Das folgende ist veraltet. Sie sollten sqlalchemy.types.Enum jetzt verwenden, wie von Wolph empfohlen. Es ist besonders schön, da es PEP-435 seit SQLAlchemy 1.1 entspricht.


Ich mag zzzeeks Rezept bei http://techspot.zzzeek.org/2011/01/14/the-enum-recipe/ , aber ich habe zwei Dinge geändert:

  • Ich benutze den Python-Namen des EnumSymbols auch als den Namen in der Datenbank, anstatt seinen Wert zu verwenden. Ich denke, das ist weniger verwirrend. Mit einem separaten Wert ist immer noch nützlich, zB zum Erstellen von Popup-Menüs in der Benutzeroberfläche. Die Beschreibung kann als eine längere Version des Wertes betrachtet werden, der zB für Tooltips verwendet werden kann.
  • In der ursprünglichen Rezeptur ist die Reihenfolge der EnumSymbols beliebig, sowohl wenn Sie über sie in Python iterieren und auch wenn Sie eine "Reihenfolge von" auf der Datenbank machen. Aber oft möchte ich eine bestimmte Ordnung haben. Also habe ich die Reihenfolge geändert, um alphabetisch zu sein, wenn man die Attribute als Strings oder Tupel oder die Reihenfolge festlegt, in der die Werte deklariert werden, wenn man die Attribute explizit als EnumSymbols setzt – das ist der gleiche Trick wie SQLAlchemy, wenn er die Spalten anordnet In DeclarativeBase Klassen.

Beispiele:

 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") 

Hier ist das modifizierte Rezept; Es verwendet die OrderedDict-Klasse, die in Python 2.7 verfügbar ist:

 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( '([AZ])', 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()) 
  • Teilen Sie sqlalchemy Modelle zwischen Flasche und anderen apps
  • Sqlalchemy: TypeError: unbeschreibliche Typerstellungsinstanz, sqlalchemy
  • Verwenden von sqlalchemy, um csv-Datei in eine Datenbank zu laden
  • Kann SQLAlchemy nicht blockiert werden?
  • SQLAlchemy, bekommen Objekt nicht an eine Session gebunden
  • Ein SQLAlchemie-Ergebnis in Flasche einstellen
  • Wie mache ich effizient ein Bulk-Insert-or-Update mit SQLAlchemy?
  • Sqlalchemy einzigartig über mehrere Spalten
  • Konvertiert ausgewählte datetime bis dato in sqlalchemy
  • Wie kann ich MySQL-Datenbank mit Python + SQLAlchemy remote verbinden?
  • Isolierung von py.test DB-Sessions in Flask-SQLAlchemy
  • Python ist die beste Programmiersprache der Welt.