当我尝试使用SQLAlchemy-Continuum扩展到SQLAlchemy从Kotti项目中对表进行版本化时遇到sqlalchemy.exc.InvalidRequestError: Implicitly combining column(...)
错误。这些表使用连接表继承方法模拟继承。基于Kotti的原始代码,我创建了最小的测试用例来显示问题(下面是test.py
)。在文件内容之后的追溯中可以看到错误,如下所示:
sqlalchemy.exc.InvalidRequestError:在属性' transaction_id'下隐式地将列contents_version.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的FAQ标题为I’m getting a warning or error about “Implicitly combining column X under attribute Y”的条目,但鉴于错误来自扩展程序,我仍然不知道在哪里修复此问题。
我在SQLAlchemy-Continuum的跟踪器here和SQLAlchemy here的邮件列表上提出了这个问题但尚未回复。
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