[Sqlalchemy-tickets] Issue #2996: Could not locate any relevant foreign key columns with mixin and
Brought to you by:
zzzeek
|
From: Charles-Axel D. <iss...@bi...> - 2014-03-18 02:24:19
|
New issue 2996: Could not locate any relevant foreign key columns with mixin and declared_attr https://bitbucket.org/zzzeek/sqlalchemy/issue/2996/could-not-locate-any-relevant-foreign-key Charles-Axel Dein: The following code: ``` #!python from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.ext.declarative import declared_attr from sqlalchemy.orm import foreign from sqlalchemy.orm import relationship from sqlalchemy.orm import sessionmaker from sqlalchemy.schema import Column, ForeignKey from sqlalchemy.types import Integer, Boolean Base = declarative_base() class Transaction(Base): __tablename__ = 'transactions' id = Column(Integer, primary_key=True) invoice_id = Column(Integer, ForeignKey('invoices.id')) class InvoiceMixin(object): __tablename__ = 'invoices' id = Column(Integer, primary_key=True) approved = Column(Boolean) @declared_attr def transactions(cls): return relationship( 'Transaction', primaryjoin=lambda: Transaction.invoice_id == cls.id # primaryjoin=lambda: remote(Transaction.invoice_id) == cls.id # primaryjoin='Transaction.invoice_id == %s.id' % cls.__name__, ) class Invoice1(InvoiceMixin, Base): __table_args__ = {'extend_existing': True} class Invoice2(InvoiceMixin, Base): __table_args__ = {'extend_existing': True} def main(): engine = create_engine('sqlite:///:memory:') Base.metadata.create_all(engine) Session = sessionmaker(bind=engine) session = Session() session.query(Invoice1).get(0) if __name__ == '__main__': main() ``` Will fail with the following: ``` #!python Traceback (most recent call last): File "reproduce_sqla_bug.py", line 53, in <module> main() File "reproduce_sqla_bug.py", line 49, in main session.query(Invoice1).get(0) File "/Users/ca/.virtualenvs/sqlalchemy/lib/python2.7/site-packages/sqlalchemy/orm/session.py", line 1151, in query return self._query_cls(entities, self, **kwargs) File "/Users/ca/.virtualenvs/sqlalchemy/lib/python2.7/site-packages/sqlalchemy/orm/query.py", line 106, in __init__ self._set_entities(entities) File "/Users/ca/.virtualenvs/sqlalchemy/lib/python2.7/site-packages/sqlalchemy/orm/query.py", line 116, in _set_entities self._set_entity_selectables(self._entities) File "/Users/ca/.virtualenvs/sqlalchemy/lib/python2.7/site-packages/sqlalchemy/orm/query.py", line 149, in _set_entity_selectables ent.setup_entity(*d[entity]) File "/Users/ca/.virtualenvs/sqlalchemy/lib/python2.7/site-packages/sqlalchemy/orm/query.py", line 2980, in setup_entity self._with_polymorphic = ext_info.with_polymorphic_mappers File "/Users/ca/.virtualenvs/sqlalchemy/lib/python2.7/site-packages/sqlalchemy/util/langhelpers.py", line 712, in __get__ obj.__dict__[self.__name__] = result = self.fget(obj) File "/Users/ca/.virtualenvs/sqlalchemy/lib/python2.7/site-packages/sqlalchemy/orm/mapper.py", line 1840, in _with_polymorphic_mappers configure_mappers() File "/Users/ca/.virtualenvs/sqlalchemy/lib/python2.7/site-packages/sqlalchemy/orm/mapper.py", line 2544, in configure_mappers mapper._post_configure_properties() File "/Users/ca/.virtualenvs/sqlalchemy/lib/python2.7/site-packages/sqlalchemy/orm/mapper.py", line 1657, in _post_configure_properties prop.init() File "/Users/ca/.virtualenvs/sqlalchemy/lib/python2.7/site-packages/sqlalchemy/orm/interfaces.py", line 143, in init self.do_init() File "/Users/ca/.virtualenvs/sqlalchemy/lib/python2.7/site-packages/sqlalchemy/orm/relationships.py", line 1512, in do_init self._setup_join_conditions() File "/Users/ca/.virtualenvs/sqlalchemy/lib/python2.7/site-packages/sqlalchemy/orm/relationships.py", line 1588, in _setup_join_conditions can_be_synced_fn=self._columns_are_mapped File "/Users/ca/.virtualenvs/sqlalchemy/lib/python2.7/site-packages/sqlalchemy/orm/relationships.py", line 1856, in __init__ self._check_foreign_cols(self.primaryjoin, True) File "/Users/ca/.virtualenvs/sqlalchemy/lib/python2.7/site-packages/sqlalchemy/orm/relationships.py", line 2374, in _check_foreign_cols raise sa_exc.ArgumentError(err) sqlalchemy.exc.ArgumentError: Could not locate any relevant foreign key columns for primary join condition 'transactions.invoice_id = invoices.id' on relationship Invoice1.transactions. Ensure that referencing columns are associated with a ForeignKey or ForeignKeyConstraint, or are annotated in the join condition with the foreign() annotation. ``` If I don't specify the `primaryjoin`, it will be ok. If I remove one of the subclass (e.g. `Invoice1`), it will be ok. My guess is that this come from `_setup_pairs`'s deannotation step. The first time the relationship, it's gonna get rid of the annotation and the next time it runs, it won't have access to them. Actually, after having a second look, maybe it's not what's happening there. Am I missing anything? Thanks a lot Mike! Responsible: zzzeek |