question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

contains_eager() silently ignored when using specific polymorphic model (failing unit test inside)

See original GitHub issue

We are observing a silently ignored contains_eager() definition in the following example, formatted into a simple unit test which is failing with SQLAlchemy==1.3.12:

from re import match
from unittest import TestCase

from sqlalchemy import Column, create_engine, ForeignKey, Integer, String
from sqlalchemy.dialects import postgresql
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import aliased, contains_eager, relationship, sessionmaker


class TestEagerLoad(TestCase):
    def test_eager_load(self):
        Base = declarative_base()
        class X(Base):
            __tablename__ = 'x'
            id = Column(Integer, primary_key=True)
            a_id = Column(Integer, ForeignKey('a.id'))
            a = relationship('A', back_populates='x')
        class A(Base):
            __tablename__ = 'a'
            id = Column(Integer, primary_key=True)
            b = relationship('B', back_populates='a')
            kind = Column(String)
            x = relationship('X', back_populates='a')
            __mapper_args__ = {
                'polymorphic_identity': 'a',
                'polymorphic_on': kind,
                'with_polymorphic': '*',
            }
        class B(A):
            a_id = Column(Integer, ForeignKey('a.id'))
            a = relationship('A', back_populates='b', uselist=False, remote_side=A.id)
            __mapper_args__ = {
                'polymorphic_identity': 'b',
            }
        engine = create_engine('sqlite://')
        Base.metadata.create_all(engine)
        session = sessionmaker(bind=engine)()
        a_x_alias = aliased(X, name='a_x')
        a_b_alias = aliased(B, name='a_b')
        b_x_alias = aliased(X, name='b_x')
        q = (
            session
            .query(A)
            .outerjoin(A.x.of_type(a_x_alias))
            .options(contains_eager(A.x.of_type(a_x_alias)))
            .outerjoin(A.b.of_type(a_b_alias))
            .outerjoin(a_b_alias.x.of_type(b_x_alias))
            .options(
                contains_eager(A.b.of_type(a_b_alias))
                .contains_eager(a_b_alias.x.of_type(b_x_alias))  # !!! Without effect !!!
            )
        )
        s = str(q.statement.compile(
            dialect=postgresql.dialect(), compile_kwargs={"literal_binds": True}
        )).replace('\n', '')
        print(s)
        self.assertCountEqual(
            [i.strip() for i in match(r'SELECT(.*)FROM', s).group(1).split(',')],
            [
                'a.id',
                'a.a_id',
                'a.kind',
                'a_x.id',
                'a_x.a_id',
                'a_b.id',
                'a_b.a_id',
                'a_b.kind',
                'b_x.id',
                'b_x.a_id',
            ]
        )

with:

E       AssertionError: Element counts were not equal:
E       First has 0, Second has 1:  'b_x.id'
E       First has 0, Second has 1:  'b_x.a_id'

which is surprising because the eager-loading of b_x is properly specified to my understanding.

The generated query is the following:

SELECT a_b.id, a_b.kind, a_b.a_id, a_x.id, a_x.a_id, a.id, a.kind, a.a_id 
FROM a
LEFT OUTER JOIN x AS a_x ON a.id = a_x.a_id
LEFT OUTER JOIN a AS a_b ON a.id = a_b.a_id AND a_b.kind IN ('b')
LEFT OUTER JOIN x AS b_x ON a_b.id = b_x.a_id

We however expected this query to be generated:

SELECT a_b.id, a_b.kind, a_b.a_id, a_x.id, a_x.a_id, a.id, a.kind, a.a_id, b_x.id, b_x.id
FROM a
LEFT OUTER JOIN x AS a_x ON a.id = a_x.a_id
LEFT OUTER JOIN a AS a_b ON a.id = a_b.a_id AND a_b.kind IN ('b')
LEFT OUTER JOIN x AS b_x ON a_b.id = b_x.a_id

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Comments:7 (3 by maintainers)

github_iconTop GitHub Comments

1reaction
larsblumbergcommented, Jan 21, 2020

Thank you Mike. PS: I donated a few bucks to your PayPal.

0reactions
zzzeekcommented, Jan 21, 2020

thank you very much !!

Read more comments on GitHub >

github_iconTop Results From Across the Web

Conditionally ignoring tests in JUnit 4 - java - Stack Overflow
To fail the test, use org.junit.Assert.fail() inside the conditional statement. Works same like Assume.assumeTrue() but fails the test.
Read more >
unittest — Unit testing framework — Python 3.11.1 ...
The crux of each test is a call to assertEqual() to check for an expected result; assertTrue() or assertFalse() to verify a condition;...
Read more >
Exception when trying to use InMemoryDatabase for unit testing
I'm trying to get xUnit working with EF's InMemoryDatabase but I'm ... are silently being ignored in tests (because InMemory is being used), ......
Read more >
Three Reasons Why We Should Not Use Inheritance In Our ...
If we use inheritance in our tests, it can have a negative effect to the performance of our test suite. In order to...
Read more >
Goto Fail, Heartbleed, and Unit Testing Culture - Martin Fowler
This article considers the role unit testing could play, showing how unit tests, and more importantly a unit testing culture, could have identified...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found