2011-04-20 39 views
1

我有一個類是SQLAlchemy的聲明基的後代。我需要編寫一個橋接對象,它將在聲明性基礎和我正在運行的另一個系統之間進行轉換,但是我希望橋對象不必確切地知道我的記錄中有哪些列。既然如此,我想要一種方法來列出記錄中的所有列,以免在兩個模糊相關的地方維護一個屬性列表,並在此期間儘量減少複製。這意味着我可以創建列屬性,然後維護一個單獨的列名稱列表,但同樣,複製是一個因素 - 如果以後我想改變這個東西的結構,我不得不在兩個地方改變它。類範圍內的動態實例變量

既然如此,我想過在一個字典粘列定義,然後遍歷字典來創建SETATTR每列的定義,像這樣:

for k,v in self.__column_dict.items(): 
     setattr(self,k, 
       sqlalchemy.Column(v['column_type'],**v.get('opts',{})), 
       ) 

現在,這裏的難題是:如果我在類級別上粘住這段代碼,self還沒有定義,並且我得到一個錯誤(這是有道理的)。如果我堅持這一點在__init__,自定義,但SQLAlchemy的聲明性基地給出了一個錯誤,因爲在類定義完成之後,表上沒有主鍵(這是有道理的,因爲這些屬性將不會被設置,直到運行)。

所以,所有的說法,我怎麼得到我想要的而不使用eval,或者使用eval我唯一的選擇,在這裏?


編輯:我接受了下面的解決方案,但沒有使用它100%(雖然它是絕對有助於讓我的答案)。下面是我落得這樣做: (注意,DeclarativeMeta是sqlalchemy.ext.declarative的一部分,同一個包declarative_base)

class MyMeta(DeclarativeMeta): 
    def __new__(meta, classname, bases, dict_): 
     klass = type.__new__(meta, classname, bases, dict_) 
     if dict_.has_key('__classinit__'): 
      klass.__classinit__ = staticmethod(klass.__classinit__.im_func) 
     klass.__classinit__(klass, dict_) 
     return klass 

然後,在我的派生類,

class DerivedClass(declarative_base()): 
    __tablename__ = 'yaddayadda' 
    __metaclass__ = MyMeta 

    def __classinit__(klass, dict_): 
     klass.__column_dict = <column dict here> 

     for k,v in klass.__column_dict.items(): 
      setattr(klass,k, 
       <fancy column stuff> 
       ) 
    ...<rest of class definition>... 

回答

1

這可通過提供一個元類來完成declarative_base 和元類

from sqlalchemy import Column, Integer 
from sqlalchemy.ext.declarative import declarative_base, DeclarativeMeta 

class MyMeta(DeclarativeMeta): 

    def __init__(klass, classname, bases, dict_): 

     for k, v in dict_.items(): 

      if k.endswith('__column_dict'): 

       for name, datatype, is_pk in v: 

        setattr(klass, name, Column(name, datatype, primary_key=is_pk)) 

     return DeclarativeMeta.__init__(klass, classname, bases, dict_) 

Base = declarative_base(metaclass=MyMeta) 

class Bob(Base): 
    __tablename__ = 'bob' 
    __column_dict = [('a', Integer, True), ('b', Integer, False)] 

bob = Bob() 
print bob.a 
print bob.b 
+0

非常感謝。可以肯定的是,我是否還應該最後稱之爲超級,以便將來打樣?例如,return super(RecordMeta,klass).__ init __(classname,bases,dict_)? – 2011-04-21 12:46:52

+0

回想起來,當我實現這個時,我得到「類型對象'MyDerivedObject'沒有屬性'_MyMeta__column_dict'。它似乎是將Record_dict的column_dict搜索綁定在一起,我如何告訴它實際看起來在哪裏? – 2011-04-21 13:04:44

+0

對不起,這是我提供的一個馬虎的例子。增加了一個新的完整的。 – GHZ 2011-04-26 09:41:35