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()) 
  • Was sollte der bevorzugte Datentyp für Primärschlüssel Colum sein, wenn in der Datenbank (MSSQL) ist es definiert als Numeric (18,0)
  • Einstellung delete-orphan auf SQLAlchemy-Beziehung verursacht AssertionError: Dieses AttributeImpl ist nicht für die Verfolgung von Eltern konfiguriert
  • Suchen Sie ein Set in SQLAlchemy Abfrage
  • Flask-SQLAlchemy prüft, ob die Zeile in der Tabelle existiert
  • SQLAlchemy Union Parenthesis Ausgabe
  • Dump csv von sqlalchemy
  • Hat Kontext / Scoping einer SQLAlchemy-Sitzung einen nicht-automatischen Objekt- / Attributablauf erforderlich?
  • Warum ist meine scoped_session, die ein AttributeError aufhebt: 'Session' Objekt hat kein Attribut 'remove'
  • Sqlalchemy-Abfrage liefert falsche und veraltete Ergebnisse (für sqlite-Engine)
  • SQLAlchemy Modell Rundschreiben Import
  • Sqlalchemy Gleichzeitigkeit Update Problem
  • Python ist die beste Programmiersprache der Welt.