2010-04-20 12 views
30

मैं SQLAlchemy के बारे में पढ़ रहा हूँ और मैं कोड निम्नलिखित देखा:स्क्लेल्चेमी में enum करने का सबसे अच्छा तरीका?

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

मैं एक पुस्तकालय में स्थिरांक के साथ, 'प्रकार' एक पूर्णांक बना दें? या मैं सिर्फ एक enum बनाने के लिए बनाना चाहिए?

उत्तर

23

SQLAlchemy 0.6 के बाद से एक Enum प्रकार का है: http://docs.sqlalchemy.org/en/latest/core/type_basics.html?highlight=enum#sqlalchemy.types.Enum

हालांकि मैं केवल यह उपयोग अगर अपने डाटाबेस ने एक निवासी enum प्रकार है की सिफारिश करेंगे। अन्यथा मैं व्यक्तिगत रूप से सिर्फ एक int का उपयोग करता हूं।

+0

दूसरा लिंक अब और नहीं खोल रहा है – MajesticRa

+0

@MajesticRa: टिप के लिए धन्यवाद, मैंने लिंक हटा दिया है। वैसे भी यह प्रासंगिक नहीं है। प्रत्येक व्यक्ति को 0.6 साल पहले अपग्रेड करना चाहिए था;) – Wolph

45

पायथन के प्रगणित प्रकार SQLAlchemy 1.1 के रूप में SQLAlchemy Enum प्रकार से सीधे स्वीकार्य हैं:

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

ध्यान दें कि ऊपर, स्ट्रिंग मान "एक", "दो", "तीन" कायम कर रहे हैं, नहीं पूर्णांक मान

SQLAlchemy के पुराने संस्करणों के लिए, मैं एक पोस्ट जो अपनी ही प्रगणित प्रकार (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(
            '([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 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() 
+0

एचएम, लिंक अभी भी जिंदा है, लेकिन वहां पोस्ट किया जाने वाला रननेबल कोड एसक्यूएलए (1.0.9) के हाल के संस्करणों के साथ काम नहीं कर रहा है। क्या यह अभी भी enums, @zzzeek को संभालने की आपकी पसंदीदा विधि है? – achiang

+0

मैंने अभी http://techspot.zzzeek.org/files/2011/decl_enum.py पर स्क्रिप्ट चलाई है जैसा कि मास्टर के खिलाफ है और यह पूरी तरह से चलता है। – zzzeek

+0

हाय @zzzeek, ​​यह मैंने अनुभव किया है: https://gist.github.com/achiang/8d4a6e3f495084d6761c – achiang

9

नोट बनाता लिखा है: निम्नलिखित पुरानी हो चुकी है। आपको sqlalchemy.types.Enum का उपयोग करना चाहिए, जैसा कि वोल्फ द्वारा अनुशंसित किया गया है। यह विशेष रूप से अच्छा है क्योंकि यह एसक्यूएलकेकी 1.1 के बाद पीईपी -435 का अनुपालन करता है।


मैं http://techspot.zzzeek.org/2011/01/14/the-enum-recipe/ पर zzzeek का नुस्खा पसंद है, लेकिन मैं दो चीजें बदल: बजाय अपने मूल्य का उपयोग करने का भी डेटाबेस में नाम के रूप में

  • मैं EnumSymbol का अजगर नाम का उपयोग कर रहा हूँ। मुझे लगता है कि यह कम भ्रमित है। एक अलग मूल्य होने के बावजूद अभी भी उपयोगी है, उदा। यूआई में पॉपअप मेनू बनाने के लिए। विवरण को उस मान का लंबा संस्करण माना जा सकता है जिसका उपयोग किया जा सकता है उदा। टूलटिप्स के लिए।
  • मूल नुस्खा में, EnumSymbols का क्रम मनमाने ढंग से होता है, जब आप पाइथन में उन पर फिर से होते हैं और जब आप डेटाबेस पर "ऑर्डर" करते हैं। लेकिन अक्सर मैं एक निर्धारित आदेश चाहते हैं। इसलिए मैंने वर्णों को वर्णमाला के रूप में बदल दिया है यदि आप गुणों को स्ट्रिंग्स या टुपल्स के रूप में सेट करते हैं, या जिस क्रम में मूल्यों को घोषित किया जाता है, यदि आप स्पष्ट रूप से एंट्ससिंबल्स के रूप में विशेषताओं को सेट करते हैं - यह उसी चाल का उपयोग कर रहा है जैसे कॉलम ऑर्डर करते समय स्क्लेक्लेमी करता है DeclarativeBase कक्षाओं में।

उदाहरण:

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

यहाँ संशोधित नुस्खा है, SQLAlchemy में

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()) 
11

मैं वास्तव में जानकार नहीं हूँ लेकिन this approach by Paulo मेरे लिए बहुत सरल लग रहा था: यह OrderedDict वर्ग अजगर 2.7 में उपलब्ध उपयोग करता है।
मुझे उपयोगकर्ता के अनुकूल विवरण की आवश्यकता नहीं थी, इसलिए मैं इसके साथ गया।

का हवाला देते हुए पाउलो (मुझे आशा है कि वह मेरा इसे यहाँ reposting मन नहीं करता है): बचाव के लिए

पायथन के namedtuple संग्रह। जैसा कि नाम का तात्पर्य है, namedtuple प्रत्येक आइटम वाले नाम के साथ एक टुपल है। एक साधारण ट्यूपल की तरह, आइटम अपरिवर्तनीय हैं।सामान्य टुपल के विपरीत, डॉट नोटेशन का उपयोग करके किसी आइटम के मूल्य को उसके नाम से एक्सेस किया जा सकता है।

यहाँ एक namedtuple बनाने के लिए एक उपयोगिता समारोह है:

from collections import namedtuple 

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

* से पहले मान चर के लिए है "खोल" सूची की वस्तुओं इतना है कि प्रत्येक आइटम के लिए एक व्यक्ति के तर्क के रूप में पारित हो जाता है फ़ंक्शन।

एक namedtuple बनाने के लिए, की जरूरत मूल्यों के साथ ऊपर समारोह आह्वान:

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

अब हम संस्करण मैदान के मान निर्दिष्ट करने project_version namedtuple उपयोग कर सकते हैं।

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

यह मेरे लिए बहुत अच्छा काम करता है और मुझे पहले मिले अन्य समाधानों की तुलना में इतना आसान है।

संबंधित मुद्दे