早在2010年10月,我就将this question发布到了Sqlalchemy用户列表中。
当时,我刚刚使用了消息中提到的clear_mappers
解决方法,并没有试图找出问题所在。那对我很顽皮。今天我又遇到了这个bug,并决定构建一个最小的例子,如下所示。迈克尔也在2006年回复了probably the same issue的内容。我决定在这里跟进,让迈克尔摆脱我愚蠢的问题。
因此,结果似乎是对于给定的类定义,您不能定义多个映射器。在我的例子中,我在模块范围内声明了Pheno
类(我假设这是顶级范围),每次运行make_tables
时,它会尝试定义另一个映射器。
Mike写道“根据上述问题的描述,你需要确保你的Python类与你的映射器在同一范围内声明。你得到的错误信息表明'Pheno'是在模块级别声明的“。这会解决问题,但如何在不改变现有结构的情况下进行管理呢?我有什么其他选择,如果有的话?显然映射器没有像“如果映射器已经定义,退出而不做任何事情”这样的选项,这将很好地处理它。我想我可以定义一个包装函数,但那会非常难看。
from sqlalchemy import *
from sqlalchemy.orm import *
def make_pheno_table(meta, schema, name='pheno'):
pheno_table = Table(
name, meta,
Column('patientid', String(60), primary_key=True),
schema=schema,
)
return pheno_table
class Pheno(object):
def __init__(self, patientid):
self.patientid = patientid
def make_tables(schema):
from sqlalchemy import MetaData
meta = MetaData()
pheno_table = make_pheno_table(meta, schema)
mapper(Pheno, pheno_table)
table_dict = {'metadata': meta, 'pheno_table':pheno_table}
return table_dict
table_dict = make_tables('foo')
table_dict = make_tables('bar')
以下是错误消息。在Debian squeeze上使用SQLAlchemy 0.6.3-3进行测试。
$ python test.py
Traceback (most recent call last):
File "test.py", line 25, in <module>
table_dict = make_tables('bar')
File "test.py", line 20, in make_tables
mapper(Pheno, pheno_table)
File "/usr/lib/python2.6/dist-packages/sqlalchemy/orm/__init__.py", line 818, in mapper
return Mapper(class_, local_table, *args, **params)
File "/usr/lib/python2.6/dist-packages/sqlalchemy/orm/mapper.py", line 209, in __init__
self._configure_class_instrumentation()
File "/usr/lib/python2.6/dist-packages/sqlalchemy/orm/mapper.py", line 381, in _configure_class_instrumentation
self.class_)
sqlalchemy.exc.ArgumentError: Class '<class '__main__.Pheno'>' already has a primary mapper defined. Use non_primary=True to create a non primary Mapper. clear_mappers() will remove *all* current mappers from all classes.
编辑:根据SQLAlchemy: The mapper() API中的文档,我可以用
替换上面的mapper(Pheno, pheno_table)
from sqlalchemy.orm.exc import UnmappedClassError
try:
class_mapper(Pheno)
except UnmappedClassError:
mapper(Pheno, pheno_table)
如果未为Pheno定义映射器,则会抛出UnmappedClassError
。这至少不会在我的测试脚本中返回错误,但我还没有检查它是否真的有效。评论
EDIT2:根据Denis的建议,以下工作:
class Tables(object):
def make_tables(self, schema):
class Pheno(object):
def __init__(self, patientid):
self.patientid = patientid
from sqlalchemy import MetaData
from sqlalchemy.orm.exc import UnmappedClassError
meta = MetaData()
pheno_table = make_pheno_table(meta, schema)
mapper(Pheno, pheno_table)
table_dict = {'metadata': meta, 'pheno_table':pheno_table, 'Pheno':Pheno}
return table_dict
table_dict = Tables().make_tables('foo')
table_dict = Tables().make_tables('bar')
然而,表面上相似
# does not work
class Tables(object):
class Pheno(object):
def __init__(self, patientid):
self.patientid = patientid
def make_tables(self, schema):
from sqlalchemy import MetaData
from sqlalchemy.orm.exc import UnmappedClassError
meta = MetaData()
pheno_table = make_pheno_table(meta, schema)
mapper(self.Pheno, pheno_table)
table_dict = {'metadata': meta, 'pheno_table':pheno_table, 'Pheno':self.Pheno}
return table_dict
table_dict = Tables().make_tables('foo')
table_dict = Tables().make_tables('bar')
没有。我收到与以前相同的错误消息。
我不太了解范围问题,足以说出原因。
在这两种情况下,Pheno
类都不是在某种局部范围内吗?
答案 0 :(得分:2)
您正尝试将同一个班级Pheno
映射到2个不同的表格。 SQLAlchemy只允许每个类使用一个主映射器,以便它知道要用于session.query(Pheno)
的表。目前尚不清楚你希望从你的问题中得到什么,所以我无法提出解决方案。有两个明显的选择:
non_primary=True
参数为第二个表创建non-primary mapper并将其(mapper()
函数返回的值)传递给session.query()
而不是类。 更新:为每个表定义单独的类,您可以将其定义放入make_tables()
:
def make_tables(schema):
from sqlalchemy import MetaData
meta = MetaData()
pheno_table = make_pheno_table(meta, schema)
class Pheno(object):
def __init__(self, patientid):
self.patientid = patientid
mapper(Pheno, pheno_table)
table_dict = {'metadata': meta,
'pheno_class': Pheno,
'pheno_table':pheno_table}
return table_dict
答案 1 :(得分:0)
也许我没有完全明白你想要什么,但这个食谱在不同的__tablename中创建了相同的列__
class TBase(object):
"""Base class is a 'mixin'.
Guidelines for declarative mixins is at:
http://www.sqlalchemy.org/docs/orm/extensions/declarative.html#mixin-classes
"""
id = Column(Integer, primary_key=True)
data = Column(String(50))
def __repr__(self):
return "%s(data=%r)" % (
self.__class__.__name__, self.data
)
class T1Foo(TBase, Base):
__tablename__ = 't1'
class T2Foo(TBase, Base):
__tablename__ = 't2'
engine = create_engine('sqlite:///foo.db', echo=True)
Base.metadata.create_all(engine)
sess = sessionmaker(engine)()
sess.add_all([T1Foo(data='t1'), T1Foo(data='t2'), T2Foo(data='t3'),
T1Foo(data='t4')])
print sess.query(T1Foo).all()
print sess.query(T2Foo).all()
sess.commit()