1
from sqlalchemy.orm import Session, aliased, with_polymorphic, \
2
contains_eager, joinedload, subqueryload, relationship,\
3
subqueryload_all, joinedload_all
4
from sqlalchemy import and_
5
from sqlalchemy import testing
6
from sqlalchemy.testing import fixtures
7
from sqlalchemy.testing import assert_raises, eq_
8
from sqlalchemy.testing.schema import Column
9
from sqlalchemy.engine import default
10
from sqlalchemy.testing.entities import ComparableEntity
11
from sqlalchemy import Integer, String, ForeignKey
12
from .inheritance._poly_fixtures import Company, Person, Engineer, Manager, Boss, \
13
Machine, Paperwork, _PolymorphicFixtureBase, _Polymorphic,\
14
_PolymorphicPolymorphic, _PolymorphicUnions, _PolymorphicJoins,\
15
_PolymorphicAliasedJoins
17
class _PolymorphicTestBase(object):
18
__dialect__ = 'default'
20
def test_any_one(self):
22
any_ = Company.employees.of_type(Engineer).any(
23
Engineer.primary_language == 'cobol')
24
eq_(sess.query(Company).filter(any_).one(), self.c2)
26
def test_any_two(self):
28
calias = aliased(Company)
29
any_ = calias.employees.of_type(Engineer).any(
30
Engineer.primary_language == 'cobol')
31
eq_(sess.query(calias).filter(any_).one(), self.c2)
33
def test_any_three(self):
35
any_ = Company.employees.of_type(Boss).any(
36
Boss.golf_swing == 'fore')
37
eq_(sess.query(Company).filter(any_).one(), self.c1)
39
def test_any_four(self):
41
any_ = Company.employees.of_type(Boss).any(
42
Manager.manager_name == 'pointy')
43
eq_(sess.query(Company).filter(any_).one(), self.c1)
45
def test_any_five(self):
47
any_ = Company.employees.of_type(Engineer).any(
48
and_(Engineer.primary_language == 'cobol'))
49
eq_(sess.query(Company).filter(any_).one(), self.c2)
51
def test_join_to_subclass_one(self):
53
eq_(sess.query(Company)
54
.join(Company.employees.of_type(Engineer))
55
.filter(Engineer.primary_language == 'java').all(),
58
def test_join_to_subclass_two(self):
60
eq_(sess.query(Company)
61
.join(Company.employees.of_type(Engineer), 'machines')
62
.filter(Machine.name.ilike("%thinkpad%")).all(),
65
def test_join_to_subclass_three(self):
67
eq_(sess.query(Company, Engineer)
68
.join(Company.employees.of_type(Engineer))
69
.filter(Engineer.primary_language == 'java').count(),
73
eq_(sess.query(Company.company_id, Engineer)
74
.join(Company.employees.of_type(Engineer))
75
.filter(Engineer.primary_language == 'java').count(),
78
eq_(sess.query(Company)
79
.join(Company.employees.of_type(Engineer))
80
.filter(Engineer.primary_language == 'java').count(),
83
def test_with_polymorphic_join_compile_one(self):
87
sess.query(Company).join(
88
Company.employees.of_type(
89
with_polymorphic(Person, [Engineer, Manager], aliased=True)
92
"SELECT companies.company_id AS companies_company_id, "
93
"companies.name AS companies_name FROM companies "
96
self._polymorphic_join_target([Engineer, Manager])
100
def test_with_polymorphic_join_exec_contains_eager_one(self):
103
wp = with_polymorphic(Person, [Engineer, Manager], aliased=True)
105
sess.query(Company).join(
106
Company.employees.of_type(wp)
107
).order_by(Company.company_id, wp.person_id).\
108
options(contains_eager(Company.employees.of_type(wp))).all(),
111
self.assert_sql_count(testing.db, go, 1)
113
def test_with_polymorphic_join_exec_contains_eager_two(self):
116
wp = with_polymorphic(Person, [Engineer, Manager], aliased=True)
118
sess.query(Company).join(
119
Company.employees.of_type(wp)
120
).order_by(Company.company_id, wp.person_id).\
121
options(contains_eager(Company.employees, alias=wp)).all(),
124
self.assert_sql_count(testing.db, go, 1)
126
def test_with_polymorphic_any(self):
128
wp = with_polymorphic(Person, [Engineer], aliased=True)
130
sess.query(Company.company_id).\
132
Company.employees.of_type(wp).any(
133
wp.Engineer.primary_language == 'java')
138
def test_subqueryload_implicit_withpoly(self):
142
sess.query(Company).\
143
filter_by(company_id=1).\
144
options(subqueryload(Company.employees.of_type(Engineer))).\
146
[self._company_with_emps_fixture()[0]]
148
self.assert_sql_count(testing.db, go, 4)
150
def test_joinedload_implicit_withpoly(self):
154
sess.query(Company).\
155
filter_by(company_id=1).\
156
options(joinedload(Company.employees.of_type(Engineer))).\
158
[self._company_with_emps_fixture()[0]]
160
self.assert_sql_count(testing.db, go, 3)
162
def test_subqueryload_explicit_withpoly(self):
165
target = with_polymorphic(Person, Engineer, aliased=True)
167
sess.query(Company).\
168
filter_by(company_id=1).\
169
options(subqueryload(Company.employees.of_type(target))).\
171
[self._company_with_emps_fixture()[0]]
173
self.assert_sql_count(testing.db, go, 4)
175
def test_joinedload_explicit_withpoly(self):
178
target = with_polymorphic(Person, Engineer, aliased=True)
180
sess.query(Company).\
181
filter_by(company_id=1).\
182
options(joinedload(Company.employees.of_type(target))).\
184
[self._company_with_emps_fixture()[0]]
186
self.assert_sql_count(testing.db, go, 3)
188
class PolymorphicPolymorphicTest(_PolymorphicTestBase, _PolymorphicPolymorphic):
189
def _polymorphic_join_target(self, cls):
190
from sqlalchemy.orm import class_mapper
192
m, sel = class_mapper(Person)._with_polymorphic_args(cls)
194
comp_sel = sel.compile(dialect=default.DefaultDialect())
197
comp_sel.process(sel, asfrom=True).replace("\n", "") + \
198
" ON companies.company_id = anon_1.people_company_id"
200
class PolymorphicUnionsTest(_PolymorphicTestBase, _PolymorphicUnions):
202
def _polymorphic_join_target(self, cls):
203
from sqlalchemy.orm import class_mapper
205
sel = class_mapper(Person)._with_polymorphic_selectable.element
206
comp_sel = sel.compile(dialect=default.DefaultDialect())
209
comp_sel.process(sel, asfrom=True).replace("\n", "") + \
210
" AS anon_1 ON companies.company_id = anon_1.company_id"
212
class PolymorphicAliasedJoinsTest(_PolymorphicTestBase, _PolymorphicAliasedJoins):
213
def _polymorphic_join_target(self, cls):
214
from sqlalchemy.orm import class_mapper
216
sel = class_mapper(Person)._with_polymorphic_selectable.element
217
comp_sel = sel.compile(dialect=default.DefaultDialect())
220
comp_sel.process(sel, asfrom=True).replace("\n", "") + \
221
" AS anon_1 ON companies.company_id = anon_1.people_company_id"
223
class PolymorphicJoinsTest(_PolymorphicTestBase, _PolymorphicJoins):
224
def _polymorphic_join_target(self, cls):
225
from sqlalchemy.orm import class_mapper
227
sel = class_mapper(Person)._with_polymorphic_selectable.alias()
228
comp_sel = sel.compile(dialect=default.DefaultDialect())
231
comp_sel.process(sel, asfrom=True).replace("\n", "") + \
232
" ON companies.company_id = anon_1.people_company_id"
235
class SubclassRelationshipTest(testing.AssertsCompiledSQL, fixtures.DeclarativeMappedTest):
236
"""There's overlap here vs. the ones above."""
238
run_setup_classes = 'once'
239
run_setup_mappers = 'once'
242
__dialect__ = 'default'
245
def setup_classes(cls):
246
Base = cls.DeclarativeBasic
247
class Job(ComparableEntity, Base):
248
__tablename__ = "job"
250
id = Column(Integer, primary_key=True,
251
test_needs_autoincrement=True)
252
type = Column(String(10))
253
container_id = Column(Integer, ForeignKey('data_container.id'))
254
__mapper_args__ = {"polymorphic_on": type}
257
__tablename__ = 'subjob'
258
id = Column(Integer, ForeignKey('job.id'), primary_key=True)
259
attr = Column(String(10))
260
__mapper_args__ = {"polymorphic_identity": "sub"}
262
class ParentThing(ComparableEntity, Base):
263
__tablename__ = 'parent'
264
id = Column(Integer, primary_key=True,
265
test_needs_autoincrement=True)
266
container_id = Column(Integer, ForeignKey('data_container.id'))
267
container = relationship("DataContainer")
269
class DataContainer(ComparableEntity, Base):
270
__tablename__ = "data_container"
272
id = Column(Integer, primary_key=True,
273
test_needs_autoincrement=True)
274
name = Column(String(10))
275
jobs = relationship(Job, order_by=Job.id)
278
def insert_data(cls):
279
s = Session(testing.db)
281
s.add_all(cls._fixture())
286
ParentThing, DataContainer, SubJob = \
287
cls.classes.ParentThing,\
288
cls.classes.DataContainer,\
292
container=DataContainer(name="d1",
299
container=DataContainer(name="d2",
308
def _dc_fixture(cls):
309
return [p.container for p in cls._fixture()]
311
def test_contains_eager_wpoly(self):
312
ParentThing, DataContainer, Job, SubJob = \
313
self.classes.ParentThing,\
314
self.classes.DataContainer,\
318
Job_P = with_polymorphic(Job, SubJob, aliased=True)
320
s = Session(testing.db)
321
q = s.query(DataContainer).\
322
join(DataContainer.jobs.of_type(Job_P)).\
323
options(contains_eager(DataContainer.jobs.of_type(Job_P)))
329
self.assert_sql_count(testing.db, go, 1)
331
def test_joinedload_wpoly(self):
332
ParentThing, DataContainer, Job, SubJob = \
333
self.classes.ParentThing,\
334
self.classes.DataContainer,\
338
Job_P = with_polymorphic(Job, SubJob, aliased=True)
340
s = Session(testing.db)
341
q = s.query(DataContainer).\
342
options(joinedload(DataContainer.jobs.of_type(Job_P)))
348
self.assert_sql_count(testing.db, go, 1)
350
def test_joinedload_wsubclass(self):
351
ParentThing, DataContainer, Job, SubJob = \
352
self.classes.ParentThing,\
353
self.classes.DataContainer,\
356
s = Session(testing.db)
357
q = s.query(DataContainer).\
358
options(joinedload(DataContainer.jobs.of_type(SubJob)))
364
self.assert_sql_count(testing.db, go, 1)
366
def test_lazyload(self):
367
DataContainer = self.classes.DataContainer
368
s = Session(testing.db)
369
q = s.query(DataContainer)
375
# SELECT data container
376
# SELECT job * 2 container rows
377
# SELECT subjob * 4 rows
378
self.assert_sql_count(testing.db, go, 7)
380
def test_subquery_wsubclass(self):
381
ParentThing, DataContainer, Job, SubJob = \
382
self.classes.ParentThing,\
383
self.classes.DataContainer,\
386
s = Session(testing.db)
387
q = s.query(DataContainer).\
388
options(subqueryload(DataContainer.jobs.of_type(SubJob)))
394
self.assert_sql_count(testing.db, go, 2)
396
def test_twolevel_subqueryload_wsubclass(self):
397
ParentThing, DataContainer, Job, SubJob = \
398
self.classes.ParentThing,\
399
self.classes.DataContainer,\
402
s = Session(testing.db)
403
q = s.query(ParentThing).\
406
ParentThing.container,
407
DataContainer.jobs.of_type(SubJob)
414
self.assert_sql_count(testing.db, go, 3)
416
def test_twolevel_joinedload_wsubclass(self):
417
ParentThing, DataContainer, Job, SubJob = \
418
self.classes.ParentThing,\
419
self.classes.DataContainer,\
422
s = Session(testing.db)
423
q = s.query(ParentThing).\
426
ParentThing.container,
427
DataContainer.jobs.of_type(SubJob)
434
self.assert_sql_count(testing.db, go, 1)
436
def test_any_wpoly(self):
437
ParentThing, DataContainer, Job, SubJob = \
438
self.classes.ParentThing,\
439
self.classes.DataContainer,\
443
Job_P = with_polymorphic(Job, SubJob, aliased=True)
446
q = s.query(Job).join(DataContainer.jobs).\
448
DataContainer.jobs.of_type(Job_P).\
449
any(Job_P.id < Job.id)
451
self.assert_compile(q,
452
"SELECT job.id AS job_id, job.type AS job_type, "
454
"AS job_container_id "
455
"FROM data_container "
456
"JOIN job ON data_container.id = job.container_id "
457
"WHERE EXISTS (SELECT 1 "
458
"FROM (SELECT job.id AS job_id, job.type AS job_type, "
459
"job.container_id AS job_container_id, "
460
"subjob.id AS subjob_id, subjob.attr AS subjob_attr "
461
"FROM job LEFT OUTER JOIN subjob ON job.id = subjob.id) AS anon_1 "
462
"WHERE data_container.id = anon_1.job_container_id AND job.id > anon_1.job_id)"
465
def test_any_walias(self):
466
ParentThing, DataContainer, Job, SubJob = \
467
self.classes.ParentThing,\
468
self.classes.DataContainer,\
475
q = s.query(Job).join(DataContainer.jobs).\
477
DataContainer.jobs.of_type(Job_A).\
478
any(and_(Job_A.id < Job.id, Job_A.type=='fred'))
480
self.assert_compile(q,
481
"SELECT job.id AS job_id, job.type AS job_type, "
482
"job.container_id AS job_container_id "
483
"FROM data_container JOIN job ON data_container.id = job.container_id "
484
"WHERE EXISTS (SELECT 1 "
486
"WHERE data_container.id = job_1.container_id "
487
"AND job.id > job_1.id AND job_1.type = :type_1)"
490
def test_join_wpoly(self):
491
ParentThing, DataContainer, Job, SubJob = \
492
self.classes.ParentThing,\
493
self.classes.DataContainer,\
497
Job_P = with_polymorphic(Job, SubJob, aliased=True)
500
q = s.query(DataContainer).join(DataContainer.jobs.of_type(Job_P))
501
self.assert_compile(q,
502
"SELECT data_container.id AS data_container_id, "
503
"data_container.name AS data_container_name "
504
"FROM data_container JOIN (SELECT job.id AS job_id, "
505
"job.type AS job_type, job.container_id AS job_container_id, "
506
"subjob.id AS subjob_id, subjob.attr AS subjob_attr "
507
"FROM job LEFT OUTER JOIN subjob ON job.id = subjob.id) "
508
"AS anon_1 ON data_container.id = anon_1.job_container_id")
510
def test_join_wsubclass(self):
511
ParentThing, DataContainer, Job, SubJob = \
512
self.classes.ParentThing,\
513
self.classes.DataContainer,\
518
q = s.query(DataContainer).join(DataContainer.jobs.of_type(SubJob))
519
# note the of_type() here renders JOIN for the Job->SubJob.
520
# this is because it's using the SubJob mapper directly within
521
# query.join(). When we do joinedload() etc., we're instead
522
# doing a with_polymorphic(), and there we need the join to be
524
self.assert_compile(q,
525
"SELECT data_container.id AS data_container_id, "
526
"data_container.name AS data_container_name "
527
"FROM data_container JOIN (SELECT job.id AS job_id, "
528
"job.type AS job_type, job.container_id AS job_container_id, "
529
"subjob.id AS subjob_id, subjob.attr AS subjob_attr "
530
"FROM job JOIN subjob ON job.id = subjob.id) AS anon_1 "
531
"ON data_container.id = anon_1.job_container_id"
534
def test_join_wpoly_innerjoin(self):
535
ParentThing, DataContainer, Job, SubJob = \
536
self.classes.ParentThing,\
537
self.classes.DataContainer,\
541
Job_P = with_polymorphic(Job, SubJob, aliased=True, innerjoin=True)
544
q = s.query(DataContainer).join(DataContainer.jobs.of_type(Job_P))
545
self.assert_compile(q,
546
"SELECT data_container.id AS data_container_id, "
547
"data_container.name AS data_container_name "
548
"FROM data_container JOIN (SELECT job.id AS job_id, "
549
"job.type AS job_type, job.container_id AS job_container_id, "
550
"subjob.id AS subjob_id, subjob.attr AS subjob_attr "
551
"FROM job JOIN subjob ON job.id = subjob.id) "
552
"AS anon_1 ON data_container.id = anon_1.job_container_id")
554
def test_join_walias(self):
555
ParentThing, DataContainer, Job, SubJob = \
556
self.classes.ParentThing,\
557
self.classes.DataContainer,\
564
q = s.query(DataContainer).join(DataContainer.jobs.of_type(Job_A))
565
self.assert_compile(q,
566
"SELECT data_container.id AS data_container_id, "
567
"data_container.name AS data_container_name "
568
"FROM data_container JOIN job AS job_1 "
569
"ON data_container.id = job_1.container_id")
571
def test_join_explicit_wpoly(self):
572
ParentThing, DataContainer, Job, SubJob = \
573
self.classes.ParentThing,\
574
self.classes.DataContainer,\
578
Job_P = with_polymorphic(Job, SubJob, aliased=True)
581
q = s.query(DataContainer).join(Job_P, DataContainer.jobs)
582
self.assert_compile(q,
583
"SELECT data_container.id AS data_container_id, "
584
"data_container.name AS data_container_name "
585
"FROM data_container JOIN (SELECT job.id AS job_id, "
586
"job.type AS job_type, job.container_id AS job_container_id, "
587
"subjob.id AS subjob_id, subjob.attr AS subjob_attr "
588
"FROM job LEFT OUTER JOIN subjob ON job.id = subjob.id) "
589
"AS anon_1 ON data_container.id = anon_1.job_container_id")