sqlalchemy 动态映射

Posted

技术标签:

【中文标题】sqlalchemy 动态映射【英文标题】:sqlalchemy dynamic mapping 【发布时间】:2010-04-04 10:03:19 【问题描述】:

我有以下问题:

我有课:


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.columns[columnName],而不是 word.columnName,我在映射时没有收到任何错误,但似乎没有不工作。


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

mapper(Word, wordTable, mapperProperties)

当我加载一个 word 对象时,SQLAlchemy 会创建一个具有 word.columns['english']、word.columns['korean'] 等属性的对象,而不是将它们加载到 word.columns 字典中。因此,对于每一列,它都会创建一个新属性。而且 word.columns 字典甚至都不存在。

同样,当我尝试持久化一个单词时,SQLAlchemy 期望找到列值 在名为 word.columns['english'](字符串类型)而不是字典 word.columns 的属性中。

我不得不说我在 Python 和 SQLAlchemy 方面的经验非常有限,也许无法做到我想做的事情。

任何帮助表示赞赏,

提前致谢。

【问题讨论】:

【参考方案1】:

看来你可以直接使用属性而不是使用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-like 访问列,也是可以的。你只需要实现一个dict-like 对象。我现在将提供代码来执行此操作,尽管我认为这绝对是不必要的混乱,因为属性访问是如此干净。如果你的问题已经通过上面的方法解决了,就不要再使用下面的代码了。

你可以在 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-like

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' 

我想你会同意这一切都是不必要的复杂,没有额外的好处。

【讨论】:

感谢您花时间写出如此完整的答案。我同意第一个解决方案比第二个解决方案更干净、更优雅。我没有任何充分的理由使用 dict,除了这是我脑海中出现的“正常”事情。就像我之前说的,我是 Python 的新手,我还没有学会利用动态语言的好处。我将使用您的第一个解决方案。再次感谢:) 我无法理解这里有多少这样的帖子,但当我看到一个时,我仍然感到敬畏。感谢您为此付出了如此多的思考。一年多后,对新人仍然有帮助。为你干杯 现在,如果我想使用 kwargs 启动 Word,如 w = Word(english = 'name', korean = 'Phoo', romanian = 'nume'),那将如何工作?在我的情况下,它将继续抛出 init 错误。谢谢! 是否可以组合方法并具有一些预定义的属性/列和一些动态定义的属性/列?换句话说,我有一组预先知道的数据,我想动态添加其他数据。我玩了一点映射器,并且还在类的元数据表中添加了列,这有一半的工作,除了它们最终没有存储任何设置等效属性的数据。如果不可能的话,不会破坏交易,但我很好奇。【参考方案2】:

我使用了 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()

【讨论】:

以上是关于sqlalchemy 动态映射的主要内容,如果未能解决你的问题,请参考以下文章

Sqlalchemy 动态创建表和映射类

SQLAlchemy 中的动态类创建

SQLAlchemy(一):SQLAlchemy去连接数据库ORM介绍将ORM模型映射到数据库中

python的sqlalchemy框架

sqlalchemy 关系映射

SQLAlchemy:自动将列名映射到值