【问题标题】:"Class already has a primary mapper defined" error with SQLAlchemySQLAlchemy 出现“类已经定义了主映射器”错误
【发布时间】:2013-05-09 13:06:46
【问题描述】:

早在 2010 年 10 月,我在 Sqlalchemy 用户列表中发布了 this question。 当时,我只是使用了消息中提到的clear_mappers 解决方法,并没有尝试找出问题所在。这对我来说很淘气。今天我再次遇到了这个错误,并决定构建一个最小的示例,如下所示。早在 2006 年,Michael 还谈到了 probably the same issue。我决定在这里跟进,让 Michael 从我的愚蠢问题中解脱出来。

因此,结果似乎是对于给定的类定义,您不能定义多个映射器。在我的情况下,我在模块范围内声明了 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 挤压上使用 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:根据丹尼斯的建议,以下工作:

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 类不是在某种本地范围内吗?

【问题讨论】:

    标签: sqlalchemy


    【解决方案1】:

    您正在尝试将同一类 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
    

    【讨论】:

    • 感谢您的清晰解释。您能否建议如何“定义单独的类以映射到第二个表”,而不改变我的代码结构?
    • 谢谢。我不知道可以在函数中定义一个类。昨天我花了一些时间试图找到这方面的文档,但没有成功。你能提供一个参考吗?实际上,我的代码中 make_tables 的实际版本在一个类中 - `class Tables(object):...def make_tables(schema)...'。在那里定义类有意义吗?
    【解决方案2】:

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

    info in example sqlalchemy

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-01-14
      • 2014-07-01
      • 2013-10-09
      • 1970-01-01
      • 2011-04-25
      • 1970-01-01
      • 1970-01-01
      • 2013-09-19
      相关资源
      最近更新 更多