3

当我尝试使用 SQLAlchemy-Continuum 扩展从 Kotti 项目对表进行版本控制时,我遇到了sqlalchemy.exc.InvalidRequestError: Implicitly combining column(...)错误。这些表使用连接表继承方法对继承进行建模。基于来自 Kotti 的原始代码,我创建了显示问题的最小测试用例(test.py如下)。该错误可以在文件内容之后的回溯中看到,如下所示:

sqlalchemy.exc.InvalidRequestError:将列 contents_version.transaction_id 与属性“transaction_id”下的列 nodes_version.transaction_id 隐式组合。请为这些同名列显式配置一个或多个属性。

正如最后一个调试行所说

INFO:sqlalchemy.orm.mapper.Mapper:(DocumentVersion|documents_version) _configure_property(transaction_id, Column)

很明显,错误发生在模型transaction_id属性的配置过程中DocumentVersion。此模型由 SQLAlchemy-Continuum 扩展自动创建,以跟踪原始Document模型中的更改。我猜想 SQLAlchemy-Continuum 扩展不能正确处理这种情况(连接表继承),但我不知道如何解决这个问题。我已经阅读了 SQLAlchemy 的常见问题解答条目,标题为“我收到关于“在属性 Y 下隐式组合列 X”的警告或错误</a>,但鉴于错误来自扩展,我仍然不知道在哪里可以解决这个问题.

我在 SQLAlchemy-Continuum 的跟踪器上和 SQLAlchemy 的邮件列表上提出了这个问题,没有回复。

SQLAlchemy-Continuum 1.2.0,SQLAlchemy 1.0.8

test.py


import logging
from sqlalchemy import (Column, ForeignKey, Integer, String)
from sqlalchemy.ext.declarative import (declarative_base, declared_attr)
from sqlalchemy.orm import configure_mappers
from sqlalchemy.util import classproperty
from sqlalchemy_continuum import make_versioned

logging.basicConfig()
logging.getLogger('sqlalchemy.orm').setLevel(logging.INFO)
make_versioned(user_cls=None)

class Node(declarative_base()):
    __versioned__ = {}
    __mapper_args__ = dict(polymorphic_on='type',
        polymorphic_identity='node',
        with_polymorphic='*')

    @declared_attr
    def __tablename__(cls):
        return '{0}s'.format(cls.__name__.lower())

    id = Column(Integer(), primary_key=True)
    type = Column(String(30), nullable=False)

class Content(Node):
    __versioned__ = {}

    @classproperty
    def __mapper_args__(cls):
        return dict(polymorphic_identity=cls.__name__.lower())

    id = Column(Integer, ForeignKey('nodes.id'), primary_key=True)

class Document(Content):
    __versioned__ = {}
    id = Column(Integer(), ForeignKey('contents.id'), primary_key=True)

configure_mappers()

输出和回溯:


INFO:sqlalchemy.orm.mapper.Mapper:(Node|nodes) _configure_property(id, Column)
INFO:sqlalchemy.orm.mapper.Mapper:(Node|nodes) _configure_property(type, Column)
INFO:sqlalchemy.orm.mapper.Mapper:(Node|nodes) Identified primary key columns: ColumnSet([Column('id', Integer(), table=<nodes>, primary_key=True, nullable=False)])
INFO:sqlalchemy.orm.mapper.Mapper:(Node|nodes) constructed
INFO:sqlalchemy.orm.mapper.Mapper:(Content|contents) _configure_property(id, ColumnProperty)
INFO:sqlalchemy.orm.mapper.Mapper:(Content|contents) _configure_property(type, ColumnProperty)
INFO:sqlalchemy.orm.mapper.Mapper:(Content|contents) _configure_property(id, Column)
INFO:sqlalchemy.orm.mapper.Mapper:(Content|contents) inserting column to existing list in properties.ColumnProperty id
INFO:sqlalchemy.orm.mapper.Mapper:(Content|contents) constructed
INFO:sqlalchemy.orm.mapper.Mapper:(Document|documents) _configure_property(id, ColumnProperty)
INFO:sqlalchemy.orm.mapper.Mapper:(Document|documents) _configure_property(type, ColumnProperty)
INFO:sqlalchemy.orm.mapper.Mapper:(Document|documents) _configure_property(id, Column)
INFO:sqlalchemy.orm.mapper.Mapper:(Document|documents) inserting column to existing list in properties.ColumnProperty id
INFO:sqlalchemy.orm.mapper.Mapper:(Document|documents) constructed
INFO:sqlalchemy.orm.mapper.Mapper:(Node|nodes) _post_configure_properties() started
INFO:sqlalchemy.orm.mapper.Mapper:(Node|nodes) initialize prop id
INFO:sqlalchemy.orm.mapper.Mapper:(Node|nodes) initialize prop type
INFO:sqlalchemy.orm.mapper.Mapper:(Node|nodes) _post_configure_properties() complete
INFO:sqlalchemy.orm.mapper.Mapper:(Document|documents) _post_configure_properties() started
INFO:sqlalchemy.orm.mapper.Mapper:(Document|documents) initialize prop id
INFO:sqlalchemy.orm.mapper.Mapper:(Document|documents) initialize prop type
INFO:sqlalchemy.orm.mapper.Mapper:(Document|documents) _post_configure_properties() complete
INFO:sqlalchemy.orm.mapper.Mapper:(Content|contents) _post_configure_properties() started
INFO:sqlalchemy.orm.mapper.Mapper:(Content|contents) initialize prop id
INFO:sqlalchemy.orm.mapper.Mapper:(Content|contents) initialize prop type
INFO:sqlalchemy.orm.mapper.Mapper:(Content|contents) _post_configure_properties() complete
INFO:sqlalchemy.orm.mapper.Mapper:(Transaction|transaction) _configure_property(issued_at, Column)
INFO:sqlalchemy.orm.mapper.Mapper:(Transaction|transaction) _configure_property(id, Column)
INFO:sqlalchemy.orm.mapper.Mapper:(Transaction|transaction) _configure_property(remote_addr, Column)
INFO:sqlalchemy.orm.mapper.Mapper:(Transaction|transaction) Identified primary key columns: ColumnSet([Column('id', BigInteger(), table=<transaction>, primary_key=True, nullable=False)])
INFO:sqlalchemy.orm.mapper.Mapper:(Transaction|transaction) constructed
INFO:sqlalchemy.orm.mapper.Mapper:(NodeVersion|nodes_version) _configure_property(id, Column)
INFO:sqlalchemy.orm.mapper.Mapper:(NodeVersion|nodes_version) _configure_property(type, Column)
INFO:sqlalchemy.orm.mapper.Mapper:(NodeVersion|nodes_version) _configure_property(transaction_id, Column)
INFO:sqlalchemy.orm.mapper.Mapper:(NodeVersion|nodes_version) _configure_property(end_transaction_id, Column)
INFO:sqlalchemy.orm.mapper.Mapper:(NodeVersion|nodes_version) _configure_property(operation_type, Column)
INFO:sqlalchemy.orm.mapper.Mapper:(NodeVersion|nodes_version) Identified primary key columns: ColumnSet([Column('id', Integer(), table=<nodes_version>, primary_key=True, nullable=False), Column('transaction_id', BigInteger(), table=<nodes_version>, primary_key=True, nullable=False)])
INFO:sqlalchemy.orm.mapper.Mapper:(NodeVersion|nodes_version) constructed
INFO:sqlalchemy.orm.mapper.Mapper:(Node|nodes) _configure_property(versions, RelationshipProperty)
INFO:sqlalchemy.orm.mapper.Mapper:(Content|contents) _configure_property(versions, RelationshipProperty)
INFO:sqlalchemy.orm.mapper.Mapper:(Document|documents) _configure_property(versions, RelationshipProperty)
INFO:sqlalchemy.orm.relationships.RelationshipProperty:Node.versions setup primary join nodes.id = nodes_version.id
INFO:sqlalchemy.orm.relationships.RelationshipProperty:Node.versions setup secondary join None
INFO:sqlalchemy.orm.relationships.RelationshipProperty:Node.versions synchronize pairs [(nodes.id => nodes_version.id)]
INFO:sqlalchemy.orm.relationships.RelationshipProperty:Node.versions secondary synchronize pairs []
INFO:sqlalchemy.orm.relationships.RelationshipProperty:Node.versions local/remote pairs [(nodes.id / nodes_version.id)]
INFO:sqlalchemy.orm.relationships.RelationshipProperty:Node.versions remote columns [nodes_version.id]
INFO:sqlalchemy.orm.relationships.RelationshipProperty:Node.versions local columns [nodes.id]
INFO:sqlalchemy.orm.relationships.RelationshipProperty:Node.versions relationship direction symbol('ONETOMANY')
INFO:sqlalchemy.orm.mapper.Mapper:(NodeVersion|nodes_version) _configure_property(version_parent, RelationshipProperty)
INFO:sqlalchemy.orm.relationships.RelationshipProperty:NodeVersion.version_parent setup primary join nodes.id = nodes_version.id
INFO:sqlalchemy.orm.relationships.RelationshipProperty:NodeVersion.version_parent setup secondary join None
INFO:sqlalchemy.orm.relationships.RelationshipProperty:NodeVersion.version_parent synchronize pairs [(nodes.id => nodes_version.id)]
INFO:sqlalchemy.orm.relationships.RelationshipProperty:NodeVersion.version_parent secondary synchronize pairs []
INFO:sqlalchemy.orm.relationships.RelationshipProperty:NodeVersion.version_parent local/remote pairs [(nodes_version.id / nodes.id)]
INFO:sqlalchemy.orm.relationships.RelationshipProperty:NodeVersion.version_parent remote columns [nodes.id]
INFO:sqlalchemy.orm.relationships.RelationshipProperty:NodeVersion.version_parent local columns [nodes_version.id]
INFO:sqlalchemy.orm.relationships.RelationshipProperty:NodeVersion.version_parent relationship direction symbol('MANYTOONE')
INFO:sqlalchemy.orm.strategies.LazyLoader:NodeVersion.version_parent lazy loading clause nodes.id = :param_1
INFO:sqlalchemy.orm.strategies.LazyLoader:NodeVersion.version_parent will use query.get() to optimize instance loads
INFO:sqlalchemy.orm.strategies.LazyLoader:Node.versions lazy loading clause :param_1 = nodes_version.id
INFO:sqlalchemy.orm.mapper.Mapper:(NodeVersion|nodes_version) _configure_property(transaction, RelationshipProperty)
INFO:sqlalchemy.orm.mapper.Mapper:(ContentVersion|contents_version) _configure_property(operation_type, ColumnProperty)
INFO:sqlalchemy.orm.mapper.Mapper:(ContentVersion|contents_version) _configure_property(transaction_id, ColumnProperty)
INFO:sqlalchemy.orm.mapper.Mapper:(ContentVersion|contents_version) _configure_property(end_transaction_id, ColumnProperty)
INFO:sqlalchemy.orm.mapper.Mapper:(ContentVersion|contents_version) _configure_property(id, ColumnProperty)
INFO:sqlalchemy.orm.mapper.Mapper:(ContentVersion|contents_version) _configure_property(type, ColumnProperty)
INFO:sqlalchemy.orm.mapper.Mapper:(ContentVersion|contents_version) _configure_property(version_parent, RelationshipProperty)
INFO:sqlalchemy.orm.mapper.Mapper:(ContentVersion|contents_version) _configure_property(transaction, RelationshipProperty)
INFO:sqlalchemy.orm.mapper.Mapper:(ContentVersion|contents_version) _configure_property(id, Column)
INFO:sqlalchemy.orm.mapper.Mapper:(ContentVersion|contents_version) inserting column to existing list in properties.ColumnProperty id
INFO:sqlalchemy.orm.mapper.Mapper:(ContentVersion|contents_version) constructed
INFO:sqlalchemy.orm.mapper.Mapper:(DocumentVersion|documents_version) _configure_property(operation_type, ColumnProperty)
INFO:sqlalchemy.orm.mapper.Mapper:(DocumentVersion|documents_version) _configure_property(transaction_id, ColumnProperty)
INFO:sqlalchemy.orm.mapper.Mapper:(DocumentVersion|documents_version) _configure_property(end_transaction_id, ColumnProperty)
INFO:sqlalchemy.orm.mapper.Mapper:(DocumentVersion|documents_version) _configure_property(id, ColumnProperty)
INFO:sqlalchemy.orm.mapper.Mapper:(DocumentVersion|documents_version) _configure_property(type, ColumnProperty)
INFO:sqlalchemy.orm.mapper.Mapper:(DocumentVersion|documents_version) _configure_property(version_parent, RelationshipProperty)
INFO:sqlalchemy.orm.mapper.Mapper:(DocumentVersion|documents_version) _configure_property(transaction, RelationshipProperty)
INFO:sqlalchemy.orm.mapper.Mapper:(DocumentVersion|documents_version) _configure_property(transaction_id, Column)
Traceback (most recent call last):
  File "/home/piotr/projects/sqlalchemy-continuum/test.py", line 38, in <module>
    configure_mappers()
  File "/home/piotr/projects/sqlalchemy-continuum/env/lib/python2.7/site-packages/sqlalchemy/orm/mapper.py", line 2736, in configure_mappers
    Mapper.dispatch._for_class(Mapper).after_configured()
  File "/home/piotr/projects/sqlalchemy-continuum/env/lib/python2.7/site-packages/sqlalchemy/event/attr.py", line 218, in __call__
    fn(*args, **kw)
  File "/home/piotr/projects/sqlalchemy-continuum/env/lib/python2.7/site-packages/sqlalchemy/orm/events.py", line 550, in wrap
    fn(*arg, **kw)
  File "/home/piotr/projects/sqlalchemy-continuum/sqlalchemy_continuum/builder.py", line 165, in configure_versioned_classes
    self.build_models()
  File "/home/piotr/projects/sqlalchemy-continuum/sqlalchemy_continuum/builder.py", line 87, in build_models
    self.manager.transaction_cls
  File "/home/piotr/projects/sqlalchemy-continuum/sqlalchemy_continuum/model_builder.py", line 263, in __call__
    self.version_class = self.build_model(table)
  File "/home/piotr/projects/sqlalchemy-continuum/sqlalchemy_continuum/model_builder.py", line 250, in build_model
    args
  File "/home/piotr/projects/sqlalchemy-continuum/env/lib/python2.7/site-packages/sqlalchemy/ext/declarative/api.py", line 55, in __init__
    _as_declarative(cls, classname, cls.__dict__)
  File "/home/piotr/projects/sqlalchemy-continuum/env/lib/python2.7/site-packages/sqlalchemy/ext/declarative/base.py", line 88, in _as_declarative
    _MapperConfig.setup_mapping(cls, classname, dict_)
  File "/home/piotr/projects/sqlalchemy-continuum/env/lib/python2.7/site-packages/sqlalchemy/ext/declarative/base.py", line 103, in setup_mapping
    cfg_cls(cls_, classname, dict_)
  File "/home/piotr/projects/sqlalchemy-continuum/env/lib/python2.7/site-packages/sqlalchemy/ext/declarative/base.py", line 135, in __init__
    self._early_mapping()
  File "/home/piotr/projects/sqlalchemy-continuum/env/lib/python2.7/site-packages/sqlalchemy/ext/declarative/base.py", line 138, in _early_mapping
    self.map()
  File "/home/piotr/projects/sqlalchemy-continuum/env/lib/python2.7/site-packages/sqlalchemy/ext/declarative/base.py", line 529, in map
    **self.mapper_args
  File "<string>", line 2, in mapper
  File "/home/piotr/projects/sqlalchemy-continuum/env/lib/python2.7/site-packages/sqlalchemy/orm/mapper.py", line 627, in __init__
    self._configure_properties()
  File "/home/piotr/projects/sqlalchemy-continuum/env/lib/python2.7/site-packages/sqlalchemy/orm/mapper.py", line 1318, in _configure_properties
    setparent=True)
  File "/home/piotr/projects/sqlalchemy-continuum/env/lib/python2.7/site-packages/sqlalchemy/orm/mapper.py", line 1525, in _configure_property
    prop = self._property_from_column(key, prop)
  File "/home/piotr/projects/sqlalchemy-continuum/env/lib/python2.7/site-packages/sqlalchemy/orm/mapper.py", line 1650, in _property_from_column
    raise sa_exc.InvalidRequestError(msg)
sqlalchemy.exc.InvalidRequestError: Implicitly combining column contents_version.transaction_id with column nodes_version.transaction_id under attribute 'transaction_id'.  Please configure one or more attributes for these same-named columns explicitly.

Process finished with exit code 1
4

0 回答 0