From 606eefa5c64e14e83d81e449ca46b1585e69e800 Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Mon, 29 Jan 2018 18:07:19 -0500 Subject: [PATCH] Ensure _BundleEntity provides .mapper Fixed bug where the :class:`.Bundle` object did not correctly report upon the primary :class:`.Mapper` object represened by the bundle, if any. An immediate side effect of this issue was that the new selectinload loader strategy wouldn't work with the horizontal sharding extension. Change-Id: I54a626100b2f4da497597e8944fa8dd853de47a3 Fixes: #4175 --- doc/build/changelog/unreleased_12/4175.rst | 10 ++++++ lib/sqlalchemy/orm/query.py | 4 +++ test/ext/test_horizontal_shard.py | 36 +++++++++++++++++++++- test/orm/test_query.py | 4 +++ 4 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 doc/build/changelog/unreleased_12/4175.rst diff --git a/doc/build/changelog/unreleased_12/4175.rst b/doc/build/changelog/unreleased_12/4175.rst new file mode 100644 index 0000000000..7d215a79b7 --- /dev/null +++ b/doc/build/changelog/unreleased_12/4175.rst @@ -0,0 +1,10 @@ +.. change:: + :tags: bug, orm + :tickets: 4175 + + Fixed bug where the :class:`.Bundle` object did not + correctly report upon the primary :class:`.Mapper` object + represened by the bundle, if any. An immediate + side effect of this issue was that the new selectinload + loader strategy wouldn't work with the horizontal sharding + extension. diff --git a/lib/sqlalchemy/orm/query.py b/lib/sqlalchemy/orm/query.py index 87f58b996d..4f7b22cc92 100644 --- a/lib/sqlalchemy/orm/query.py +++ b/lib/sqlalchemy/orm/query.py @@ -3892,6 +3892,10 @@ class _BundleEntity(_QueryEntity): self.supports_single_entity = self.bundle.single_entity + @property + def mapper(self): + return self.entity_zero.mapper + @property def entities(self): entities = [] diff --git a/test/ext/test_horizontal_shard.py b/test/ext/test_horizontal_shard.py index c09753e5df..a5ea44e04d 100644 --- a/test/ext/test_horizontal_shard.py +++ b/test/ext/test_horizontal_shard.py @@ -9,6 +9,7 @@ from sqlalchemy.sql import operators from sqlalchemy.testing import fixtures from sqlalchemy.testing.engines import testing_engine from sqlalchemy.testing import eq_ +from sqlalchemy import testing # TODO: ShardTest can be turned into a base for further subclasses @@ -296,7 +297,6 @@ class ShardTest(object): class DistinctEngineShardTest(ShardTest, fixtures.TestBase): - def _init_dbs(self): db1 = testing_engine('sqlite:///shard1.db', options=dict(pool_threadlocal=True)) @@ -338,3 +338,37 @@ class AttachedFileShardTest(ShardTest, fixtures.TestBase): return stmt, params return db1, db2, db3, db4 + + +class SelectinloadRegressionTest(fixtures.DeclarativeMappedTest): + @classmethod + def setup_classes(cls): + Base = cls.DeclarativeBasic + + class Book(Base): + __tablename__ = 'book' + id = Column(Integer, primary_key=True) + pages = relationship('Page') + + class Page(Base): + __tablename__ = 'page' + id = Column(Integer, primary_key=True) + book_id = Column(ForeignKey('book.id')) + + def test_selectinload_query(self): + session = ShardedSession( + shards={"test": testing.db}, + shard_chooser=lambda *args: 'test', + id_chooser=lambda *args: None, + query_chooser=lambda *args: ['test'] + ) + + Book, Page = self.classes("Book", "Page") + book = Book() + book.pages.append(Page()) + + session.add(book) + session.commit() + + result = session.query(Book).options(selectinload('pages')).all() + eq_(result, [book]) diff --git a/test/orm/test_query.py b/test/orm/test_query.py index 3b91f5ffaf..502f1427a8 100644 --- a/test/orm/test_query.py +++ b/test/orm/test_query.py @@ -843,6 +843,10 @@ class InvalidGenerationsTest(QueryTest, AssertsCompiledSQL): is_(q._mapper_zero(), None) is_(q._entity_zero(), None) + q1 = s.query(Bundle('b1', User.id, User.name)) + is_(q1._mapper_zero(), inspect(User)) + is_(q1._entity_zero(), inspect(User)) + def test_from_statement(self): User = self.classes.User -- 2.47.2