&引用;类已定义了一个主映射器";SQLAlchemy的错误

&引用;类已定义了一个主映射器";SQLAlchemy的错误,sqlalchemy,Sqlalchemy,回到2010年10月,我发布到Sqlalchemy用户列表。 当时,我只是使用了消息中提到的clear\u mappers解决方法,没有试图找出问题所在。我真淘气。今天我再次遇到了这个bug,并决定构造一个最小的示例,如下所示。Michael还谈到了2006年的情况。我决定在这里继续,让迈克尔从我愚蠢的问题中解脱出来 因此,结果似乎是,对于给定的类定义,不能定义多个映射器。在我的例子中,我在模块作用域中声明了Pheno类(我假设这里是顶级作用域),每次运行make_tables时,它都会尝试定义

回到2010年10月,我发布到Sqlalchemy用户列表。 当时,我只是使用了消息中提到的
clear\u mappers
解决方法,没有试图找出问题所在。我真淘气。今天我再次遇到了这个bug,并决定构造一个最小的示例,如下所示。Michael还谈到了2006年的情况。我决定在这里继续,让迈克尔从我愚蠢的问题中解脱出来

因此,结果似乎是,对于给定的类定义,不能定义多个映射器。在我的例子中,我在模块作用域中声明了
Pheno
类(我假设这里是顶级作用域),每次运行
make_tables
时,它都会尝试定义另一个映射器

Mike写道:“根据上述问题的描述,您需要确保Python类的声明范围与映射器的声明范围相同。您收到的错误消息表明,‘Pheno’是在模块级别声明的。”这将解决问题,但我如何在不改变当前结构的情况下管理它?如果有的话,我还有什么其他选择?显然,mapper没有“如果已经定义了mapper,则不做任何操作就退出”这样的选项,这将很好地解决这个问题。我想我可以定义一个包装器函数,但那会很难看

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挤压机上使用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.
如果没有为Pheno定义映射器,它将抛出一个
未映射类错误。这至少不会在我的测试脚本中返回错误,但我还没有检查它是否真的有效。评论

编辑2:根据丹尼斯的建议,以下工作:

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
类不是都在某种局部范围内吗?

您正在尝试将同一类
Pheno
映射到两个不同的表。SQLAlchemy只允许每个类有一个主映射器,这样它就知道会话查询(Pheno)
使用哪个表。不清楚您希望从您的问题中得到什么,所以我无法提出解决方案。有两个明显的选择:

  • 定义要映射到第二个表的单独类
  • 通过传递
    non_primary=True
    参数来创建第二个表,并将其(映射器()返回的值函数)传递给
    session.query()
    而不是类
更新:要为每个表定义单独的类,可以将其定义放入
make_tables()


也许我不太明白你想要什么,但是这个配方在不同的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()

感谢您的明确解释。你能建议如何“定义单独的类映射到第二个表”,而不改变我的代码结构吗?谢谢。我不知道可以在函数中定义类。昨天我花了一些时间试图找到这方面的文档,但没有成功。你能提供一份推荐信吗?实际上,在我的代码中,
make_tables
的实际版本在一个类中-`class tables(object):…def make_tables(schema)。在里面定义类有意义吗?
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
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()