summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormike bayer <mike_mp@zzzcomputing.com>2018-12-01 16:52:22 +0000
committerGerrit Code Review <gerrit@bbpush.zzzcomputing.com>2018-12-01 16:52:22 +0000
commitc8dea359db9bea58dc64880d306dbee2a26df247 (patch)
tree85525c1e66dc38077989c2fb4b1400b34cb2a7d1
parent7940e7dc9c40b80b103400bad6e9aac2ce8824bd (diff)
parentfb05e085fe48a1c48fcb4bcf89ba58c0d7584b8c (diff)
downloadsqlalchemy-c8dea359db9bea58dc64880d306dbee2a26df247.tar.gz
Merge "Apply path generation for superclasses to Load._set_path_strategy()"
-rw-r--r--doc/build/changelog/unreleased_13/4373.rst11
-rw-r--r--lib/sqlalchemy/orm/strategy_options.py12
-rw-r--r--test/orm/test_deferred.py119
3 files changed, 137 insertions, 5 deletions
diff --git a/doc/build/changelog/unreleased_13/4373.rst b/doc/build/changelog/unreleased_13/4373.rst
new file mode 100644
index 000000000..91afd247b
--- /dev/null
+++ b/doc/build/changelog/unreleased_13/4373.rst
@@ -0,0 +1,11 @@
+.. change::
+ :tags: bug, orm
+ :tickets: 4373
+
+ Extended the fix first made as part of :ticket:`3287`, where a loader option
+ made against a subclass using a wildcard would extend itself to include
+ application of the wildcard to attributes on the super classes as well, to a
+ "bound" loader option as well, e.g. in an expression like
+ ``Load(SomeSubClass).load_only('foo')``. Columns that are part of the
+ parent class of ``SomeSubClass`` will also be excluded in the same way as if
+ the unbound option ``load_only('foo')`` were used.
diff --git a/lib/sqlalchemy/orm/strategy_options.py b/lib/sqlalchemy/orm/strategy_options.py
index af9bd71b6..0ef34b0b9 100644
--- a/lib/sqlalchemy/orm/strategy_options.py
+++ b/lib/sqlalchemy/orm/strategy_options.py
@@ -357,9 +357,15 @@ class Load(Generative, MapperOption):
else:
effective_path = self.path
- self._set_for_path(
- self.context, effective_path, replace=True,
- merge_opts=self.is_opts_only)
+ if effective_path.is_token:
+ for path in effective_path.generate_for_superclasses():
+ self._set_for_path(
+ self.context, path, replace=True,
+ merge_opts=self.is_opts_only)
+ else:
+ self._set_for_path(
+ self.context, effective_path, replace=True,
+ merge_opts=self.is_opts_only)
def __getstate__(self):
d = self.__dict__.copy()
diff --git a/test/orm/test_deferred.py b/test/orm/test_deferred.py
index 97782bffd..3f2a8a06b 100644
--- a/test/orm/test_deferred.py
+++ b/test/orm/test_deferred.py
@@ -913,6 +913,12 @@ class SelfReferentialMultiPathTest(testing.fixtures.DeclarativeMappedTest):
class InheritanceTest(_Polymorphic):
__dialect__ = 'default'
+ @classmethod
+ def setup_mappers(cls):
+ super(InheritanceTest, cls).setup_mappers()
+ from sqlalchemy import inspect
+ inspect(Company).add_property("managers", relationship(Manager))
+
def test_load_only_subclass(self):
s = Session()
q = s.query(Manager).order_by(Manager.person_id).\
@@ -929,6 +935,22 @@ class InheritanceTest(_Polymorphic):
"ORDER BY managers.person_id"
)
+ def test_load_only_subclass_bound(self):
+ s = Session()
+ q = s.query(Manager).order_by(Manager.person_id).\
+ options(Load(Manager).load_only("status", "manager_name"))
+ self.assert_compile(
+ q,
+ "SELECT managers.person_id AS managers_person_id, "
+ "people.person_id AS people_person_id, "
+ "people.type AS people_type, "
+ "managers.status AS managers_status, "
+ "managers.manager_name AS managers_manager_name "
+ "FROM people JOIN managers "
+ "ON people.person_id = managers.person_id "
+ "ORDER BY managers.person_id"
+ )
+
def test_load_only_subclass_and_superclass(self):
s = Session()
q = s.query(Boss).order_by(Person.person_id).\
@@ -945,6 +967,22 @@ class InheritanceTest(_Polymorphic):
"ON managers.person_id = boss.boss_id ORDER BY people.person_id"
)
+ def test_load_only_subclass_and_superclass_bound(self):
+ s = Session()
+ q = s.query(Boss).order_by(Person.person_id).\
+ options(Load(Boss).load_only("status", "manager_name"))
+ self.assert_compile(
+ q,
+ "SELECT managers.person_id AS managers_person_id, "
+ "people.person_id AS people_person_id, "
+ "people.type AS people_type, "
+ "managers.status AS managers_status, "
+ "managers.manager_name AS managers_manager_name "
+ "FROM people JOIN managers "
+ "ON people.person_id = managers.person_id JOIN boss "
+ "ON managers.person_id = boss.boss_id ORDER BY people.person_id"
+ )
+
def test_load_only_alias_subclass(self):
s = Session()
m1 = aliased(Manager, flat=True)
@@ -962,6 +1000,23 @@ class InheritanceTest(_Polymorphic):
"ORDER BY managers_1.person_id"
)
+ def test_load_only_alias_subclass_bound(self):
+ s = Session()
+ m1 = aliased(Manager, flat=True)
+ q = s.query(m1).order_by(m1.person_id).\
+ options(Load(m1).load_only("status", "manager_name"))
+ self.assert_compile(
+ q,
+ "SELECT managers_1.person_id AS managers_1_person_id, "
+ "people_1.person_id AS people_1_person_id, "
+ "people_1.type AS people_1_type, "
+ "managers_1.status AS managers_1_status, "
+ "managers_1.manager_name AS managers_1_manager_name "
+ "FROM people AS people_1 JOIN managers AS "
+ "managers_1 ON people_1.person_id = managers_1.person_id "
+ "ORDER BY managers_1.person_id"
+ )
+
def test_load_only_subclass_from_relationship_polymorphic(self):
s = Session()
wp = with_polymorphic(Person, [Manager], flat=True)
@@ -984,10 +1039,30 @@ class InheritanceTest(_Polymorphic):
"people_1.company_id"
)
+ def test_load_only_subclass_from_relationship_polymorphic_bound(self):
+ s = Session()
+ wp = with_polymorphic(Person, [Manager], flat=True)
+ q = s.query(Company).join(Company.employees.of_type(wp)).options(
+ Load(Company).contains_eager(Company.employees.of_type(wp)).
+ load_only(wp.Manager.status, wp.Manager.manager_name)
+ )
+ self.assert_compile(
+ q,
+ "SELECT people_1.person_id AS people_1_person_id, "
+ "people_1.type AS people_1_type, "
+ "managers_1.person_id AS managers_1_person_id, "
+ "managers_1.status AS managers_1_status, "
+ "managers_1.manager_name AS managers_1_manager_name, "
+ "companies.company_id AS companies_company_id, "
+ "companies.name AS companies_name "
+ "FROM companies JOIN (people AS people_1 LEFT OUTER JOIN "
+ "managers AS managers_1 ON people_1.person_id = "
+ "managers_1.person_id) ON companies.company_id = "
+ "people_1.company_id"
+ )
+
def test_load_only_subclass_from_relationship(self):
s = Session()
- from sqlalchemy import inspect
- inspect(Company).add_property("managers", relationship(Manager))
q = s.query(Company).join(Company.managers).options(
contains_eager(Company.managers).
load_only("status", "manager_name")
@@ -1005,10 +1080,31 @@ class InheritanceTest(_Polymorphic):
"managers.person_id) ON companies.company_id = people.company_id"
)
+ def test_load_only_subclass_from_relationship_bound(self):
+ s = Session()
+ q = s.query(Company).join(Company.managers).options(
+ Load(Company).contains_eager(Company.managers).
+ load_only("status", "manager_name")
+ )
+ self.assert_compile(
+ q,
+ "SELECT companies.company_id AS companies_company_id, "
+ "companies.name AS companies_name, "
+ "managers.person_id AS managers_person_id, "
+ "people.person_id AS people_person_id, "
+ "people.type AS people_type, "
+ "managers.status AS managers_status, "
+ "managers.manager_name AS managers_manager_name "
+ "FROM companies JOIN (people JOIN managers ON people.person_id = "
+ "managers.person_id) ON companies.company_id = people.company_id"
+ )
+
def test_defer_on_wildcard_subclass(self):
# pretty much the same as load_only except doesn't
# exclude the primary key
+ # TODO: what is ".*"? this is not documented anywhere, how did this
+ # get implemented without docs ? see #4390
s = Session()
q = s.query(Manager).order_by(Person.person_id).options(
defer(".*"), undefer("status"))
@@ -1019,6 +1115,9 @@ class InheritanceTest(_Polymorphic):
"people.person_id = managers.person_id ORDER BY people.person_id"
)
+ # note this doesn't apply to "bound" loaders since they don't seem
+ # to have this ".*" featue.
+
def test_defer_super_name_on_subclass(self):
s = Session()
q = s.query(Manager).order_by(Person.person_id).options(defer("name"))
@@ -1034,6 +1133,22 @@ class InheritanceTest(_Polymorphic):
"ORDER BY people.person_id"
)
+ def test_defer_super_name_on_subclass_bound(self):
+ s = Session()
+ q = s.query(Manager).order_by(Person.person_id).options(
+ Load(Manager).defer("name"))
+ self.assert_compile(
+ q,
+ "SELECT managers.person_id AS managers_person_id, "
+ "people.person_id AS people_person_id, "
+ "people.company_id AS people_company_id, "
+ "people.type AS people_type, managers.status AS managers_status, "
+ "managers.manager_name AS managers_manager_name "
+ "FROM people JOIN managers "
+ "ON people.person_id = managers.person_id "
+ "ORDER BY people.person_id"
+ )
+
class WithExpressionTest(fixtures.DeclarativeMappedTest):