When I tried to version tables from Kotti project using SQLAlchemy-Continuum extension to SQLAlchemy I encountered sqlalchemy.exc.InvalidRequestError: Implicitly combining column(...) error. These tables model inheritance using joined table inheritance approach. Based on the original code from Kotti I created minimal test case showing the problem (test.py below). The error can be seen in traceback coming after the file's content and looks like this:

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.

As the last debug line says

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

it's clear that the error happens during configuration of transaction_id attribute of DocumentVersion model. This model is being automatically created by SQLAlchemy-Continuum extension to track changes in the original Document model. I guess that SQLAlchemy-Continuum extension does not handle this scenario (joined table inheritance) correctly but I have no idea how I could fix this. I've read SQLAlchemy's FAQ entry titled I’m getting a warning or error about “Implicitly combining column X under attribute Y” but given the error comes from the extension I still don't know where to look to fix this.

I raised this issue on SQLAlchemy-Continuum's tracker here and on mailing list of SQLAlchemy here but with no replies yet.

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()

Output and traceback:


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
0

There are 0 answers