2010-04-04 91 views
10

我有以下問題:SQLAlchemy的動態映射

我有類:


class Word(object): 

    def __init__(self): 
     self.id = None 
     self.columns = {} 

    def __str__(self): 
     return "(%s, %s)" % (str(self.id), str(self.columns)) 

self.columns是將保持一個字典(COLUMNNAME:columnValue)值。列的名稱在運行時是已知的,它們被裝在一個wordColumns列表,例如


wordColumns = ['english', 'korean', 'romanian'] 

wordTable = Table('word', metadata, 
        Column('id', Integer, primary_key = True) 
       ) 
for columnName in wordColumns: 
    wordTable.append_column(Column(columnName, String(255), nullable = False)) 

我甚至創建的「力」的表列上的字被映射一個明確的映射屬性。列[列名],而不是word.columnName,我沒有得到映射的任何錯誤,但似乎不起作用。


mapperProperties = {} 
for column in wordColumns: 
    mapperProperties['columns[\'%']' % column] = wordTable.columns[column] 

mapper(Word, wordTable, mapperProperties) 

當我裝入一個字對象,SQLAlchemy的創建其具有word.columns [「英語」],word.columns [「韓語」]等性質而不是將它們加載到word.columns的字典對象。所以對於每一列,它創建一個新的屬性。此外word.columns字典甚至不存在。

同樣,當我嘗試堅持一個單詞時,SQLAlchemy希望在名爲like word.columns ['english'](string type)的屬性中找到列值 ,而不是字典word.columns。

我不得不說,我與Python和SQLAlchemy的經驗是相當有限的,也許這是不可能做什麼,我試圖做的。

任何幫助讚賞,

在此先感謝。

回答

29

看來你可以只使用屬性,而不是直接使用columnsdict的。

考慮以下設置:

from sqlalchemy import Table, Column, Integer, Unicode, MetaData, create_engine 
from sqlalchemy.orm import mapper, create_session 

class Word(object): 
    pass 

wordColumns = ['english', 'korean', 'romanian'] 
e = create_engine('sqlite://') 
metadata = MetaData(bind=e) 

t = Table('words', metadata, Column('id', Integer, primary_key=True), 
    *(Column(wordCol, Unicode(255)) for wordCol in wordColumns)) 
metadata.create_all() 
mapper(Word, t) 
session = create_session(bind=e, autocommit=False, autoflush=True) 

隨着那空類,你可以這樣做:

w = Word() 
w.english = u'name' 
w.korean = u'이름' 
w.romanian = u'nume' 

session.add(w) 
session.commit() 

而且當你要訪問的數據:

w = session.query(Word).filter_by(english=u'name').one() 
print w.romanian 

這就是整個sqlalchemy的ORM點,而不是使用一個tupledict訪問數據,您使用在自己的類屬性類訪問。

所以我想知道你想使用dict的原因。也許是因爲你的語言名稱的字符串。嗯,你可以使用Python的getattrsetattr代替,就像任何的Python對象:

language = 'korean' 
print getattr(w, language) 

這應該解決所有的問題。


也就是說,如果你仍然想使用dict式的訪問列,這也是可能的。你只需要實現一個類似於dict的對象。我現在提供的代碼要做到這一點,儘管我認爲這是絕對不必要的混亂,因爲屬性的訪問是那麼幹淨。如果您的問題已使用上述方法解決,請不要使用下面的代碼。

你可以做它的Word類:

class Word(object): 
    def __getitem__(self, item): 
     return getattr(self, item) 
    def __setitem__(self, item, value): 
     return setattr(self, item, value) 

設置的其餘部分可以作爲以上。然後,你可以使用這樣的:

w = Word() 
w['english'] = u'name' 

如果你想有一個columns屬性,那麼你需要一個dict

class AttributeDict(DictMixin): 
    def __init__(self, obj): 
     self._obj = obj 
    def __getitem__(self, item): 
     return getattr(self._obj, item) 
    def __setitem__(self, item, value): 
     return setattr(self._obj, item, value) 

class Word(object): 
    def __init__(self): 
     self.columns = AttributeDict(self) 

那麼你可以使用你想要的結果:

w = Word() 
w.columns['english'] = u'name' 

我認爲你會同意這一切都是不必要的複雜,沒有額外的好處。

+2

感謝您抽出時間來寫這樣一個完整的答案。我同意第一個解決方案比第二個解決方案更乾淨更優雅。我沒有任何好的理由使用的字典,除了一個事實,這是它排在我的腦海裏「正常」的事情。就像我之前說的,我在Python一種新的,我還沒有學到尚未採取的動態語言的優勢。我會用你的第一個解決方案。再次感謝:) – 2010-04-04 18:37:36

+3

我不能克服很多這樣的帖子怎麼會在這裏的存在,但我仍然敬畏,當我看到一個。感謝您爲此付出瞭如此多的考慮。一年之後對新人們仍然有幫助。歡呼聲 – Profane 2011-06-20 22:27:48

+0

現在,如果我想用kwargs來啓動Word,如w = Word(英文='name',korean ='Phoo',romanian ='nume'),那麼這將如何工作?在我的情況下,它會繼續拋出__init__錯誤。謝謝! – Julius 2016-01-25 23:02:51

3

我用nosklo的解決方案(感謝!),但我已經有列線(CSV的第一行)內的主鍵(傳過來pk_col)。所以我想我會分享我的修改。我用了一個三元組。

table = Table(tablename, metadata, 
    *((Column(pk_col, Integer, primary_key=True)) if rowname == pk_col else (Column(rowname, String())) for rowname in row.keys())) 
table.create()