mirror of
https://github.com/sqlalchemy/sqlalchemy.git
synced 2026-06-04 06:48:27 -04:00
2829092cb8
when placing SQL expressions in the query() list of entities. In particular scalar subqueries should not "leak" their inner FROM objects out into the enclosing query.
895 lines
42 KiB
Python
895 lines
42 KiB
Python
"""tests the Query object's ability to work with polymorphic selectables
|
|
and inheriting mappers."""
|
|
|
|
# TODO: under construction !
|
|
|
|
import testenv; testenv.configure_for_tests()
|
|
import sets
|
|
from sqlalchemy import *
|
|
from sqlalchemy.orm import *
|
|
from sqlalchemy import exc as sa_exc
|
|
from testlib import *
|
|
from testlib import fixtures
|
|
from sqlalchemy.ext.declarative import declarative_base
|
|
from sqlalchemy.engine import default
|
|
|
|
class Company(fixtures.Base):
|
|
pass
|
|
|
|
class Person(fixtures.Base):
|
|
pass
|
|
class Engineer(Person):
|
|
pass
|
|
class Manager(Person):
|
|
pass
|
|
class Boss(Manager):
|
|
pass
|
|
|
|
class Machine(fixtures.Base):
|
|
pass
|
|
|
|
class Paperwork(fixtures.Base):
|
|
pass
|
|
|
|
def make_test(select_type):
|
|
class PolymorphicQueryTest(ORMTest, AssertsCompiledSQL):
|
|
keep_data = True
|
|
keep_mappers = True
|
|
|
|
def define_tables(self, metadata):
|
|
global companies, people, engineers, managers, boss, paperwork, machines
|
|
|
|
companies = Table('companies', metadata,
|
|
Column('company_id', Integer, Sequence('company_id_seq', optional=True), primary_key=True),
|
|
Column('name', String(50)))
|
|
|
|
people = Table('people', metadata,
|
|
Column('person_id', Integer, Sequence('person_id_seq', optional=True), primary_key=True),
|
|
Column('company_id', Integer, ForeignKey('companies.company_id')),
|
|
Column('name', String(50)),
|
|
Column('type', String(30)))
|
|
|
|
engineers = Table('engineers', metadata,
|
|
Column('person_id', Integer, ForeignKey('people.person_id'), primary_key=True),
|
|
Column('status', String(30)),
|
|
Column('engineer_name', String(50)),
|
|
Column('primary_language', String(50)),
|
|
)
|
|
|
|
machines = Table('machines', metadata,
|
|
Column('machine_id', Integer, primary_key=True),
|
|
Column('name', String(50)),
|
|
Column('engineer_id', Integer, ForeignKey('engineers.person_id')))
|
|
|
|
managers = Table('managers', metadata,
|
|
Column('person_id', Integer, ForeignKey('people.person_id'), primary_key=True),
|
|
Column('status', String(30)),
|
|
Column('manager_name', String(50))
|
|
)
|
|
|
|
boss = Table('boss', metadata,
|
|
Column('boss_id', Integer, ForeignKey('managers.person_id'), primary_key=True),
|
|
Column('golf_swing', String(30)),
|
|
)
|
|
|
|
paperwork = Table('paperwork', metadata,
|
|
Column('paperwork_id', Integer, primary_key=True),
|
|
Column('description', String(50)),
|
|
Column('person_id', Integer, ForeignKey('people.person_id')))
|
|
|
|
clear_mappers()
|
|
|
|
mapper(Company, companies, properties={
|
|
'employees':relation(Person, order_by=people.c.person_id)
|
|
})
|
|
|
|
mapper(Machine, machines)
|
|
|
|
if select_type == '':
|
|
person_join = manager_join = None
|
|
person_with_polymorphic = None
|
|
manager_with_polymorphic = None
|
|
elif select_type == 'Polymorphic':
|
|
person_join = manager_join = None
|
|
person_with_polymorphic = '*'
|
|
manager_with_polymorphic = '*'
|
|
elif select_type == 'Unions':
|
|
person_join = polymorphic_union(
|
|
{
|
|
'engineer':people.join(engineers),
|
|
'manager':people.join(managers),
|
|
}, None, 'pjoin')
|
|
|
|
manager_join = people.join(managers).outerjoin(boss)
|
|
person_with_polymorphic = ([Person, Manager, Engineer], person_join)
|
|
manager_with_polymorphic = ('*', manager_join)
|
|
elif select_type == 'AliasedJoins':
|
|
person_join = people.outerjoin(engineers).outerjoin(managers).select(use_labels=True).alias('pjoin')
|
|
manager_join = people.join(managers).outerjoin(boss).select(use_labels=True).alias('mjoin')
|
|
person_with_polymorphic = ([Person, Manager, Engineer], person_join)
|
|
manager_with_polymorphic = ('*', manager_join)
|
|
elif select_type == 'Joins':
|
|
person_join = people.outerjoin(engineers).outerjoin(managers)
|
|
manager_join = people.join(managers).outerjoin(boss)
|
|
person_with_polymorphic = ([Person, Manager, Engineer], person_join)
|
|
manager_with_polymorphic = ('*', manager_join)
|
|
|
|
|
|
# testing a order_by here as well; the surrogate mapper has to adapt it
|
|
mapper(Person, people,
|
|
with_polymorphic=person_with_polymorphic,
|
|
polymorphic_on=people.c.type, polymorphic_identity='person', order_by=people.c.person_id,
|
|
properties={
|
|
'paperwork':relation(Paperwork, order_by=paperwork.c.paperwork_id)
|
|
})
|
|
mapper(Engineer, engineers, inherits=Person, polymorphic_identity='engineer', properties={
|
|
'machines':relation(Machine, order_by=machines.c.machine_id)
|
|
})
|
|
mapper(Manager, managers, with_polymorphic=manager_with_polymorphic,
|
|
inherits=Person, polymorphic_identity='manager')
|
|
mapper(Boss, boss, inherits=Manager, polymorphic_identity='boss')
|
|
mapper(Paperwork, paperwork)
|
|
|
|
|
|
def insert_data(self):
|
|
global all_employees, c1_employees, c2_employees, e1, e2, b1, m1, e3, c1, c2
|
|
|
|
c1 = Company(name="MegaCorp, Inc.")
|
|
c2 = Company(name="Elbonia, Inc.")
|
|
e1 = Engineer(name="dilbert", engineer_name="dilbert", primary_language="java", status="regular engineer", paperwork=[
|
|
Paperwork(description="tps report #1"),
|
|
Paperwork(description="tps report #2")
|
|
], machines=[
|
|
Machine(name='IBM ThinkPad'),
|
|
Machine(name='IPhone'),
|
|
])
|
|
e2 = Engineer(name="wally", engineer_name="wally", primary_language="c++", status="regular engineer", paperwork=[
|
|
Paperwork(description="tps report #3"),
|
|
Paperwork(description="tps report #4")
|
|
], machines=[
|
|
Machine(name="Commodore 64")
|
|
])
|
|
b1 = Boss(name="pointy haired boss", golf_swing="fore", manager_name="pointy", status="da boss", paperwork=[
|
|
Paperwork(description="review #1"),
|
|
])
|
|
m1 = Manager(name="dogbert", manager_name="dogbert", status="regular manager", paperwork=[
|
|
Paperwork(description="review #2"),
|
|
Paperwork(description="review #3")
|
|
])
|
|
c1.employees = [e1, e2, b1, m1]
|
|
|
|
e3 = Engineer(name="vlad", engineer_name="vlad", primary_language="cobol", status="elbonian engineer", paperwork=[
|
|
Paperwork(description='elbonian missive #3')
|
|
], machines=[
|
|
Machine(name="Commodore 64"),
|
|
Machine(name="IBM 3270")
|
|
])
|
|
|
|
c2.employees = [e3]
|
|
sess = create_session()
|
|
sess.save(c1)
|
|
sess.save(c2)
|
|
sess.flush()
|
|
sess.clear()
|
|
|
|
all_employees = [e1, e2, b1, m1, e3]
|
|
c1_employees = [e1, e2, b1, m1]
|
|
c2_employees = [e3]
|
|
|
|
def test_loads_at_once(self):
|
|
"""test that all objects load from the full query, when with_polymorphic is used"""
|
|
|
|
sess = create_session()
|
|
def go():
|
|
self.assertEquals(sess.query(Person).all(), all_employees)
|
|
self.assert_sql_count(testing.db, go, {'':14, 'Polymorphic':9}.get(select_type, 10))
|
|
|
|
def test_primary_eager_aliasing(self):
|
|
sess = create_session()
|
|
|
|
def go():
|
|
self.assertEquals(sess.query(Person).options(eagerload(Engineer.machines))[1:3], all_employees[1:3])
|
|
self.assert_sql_count(testing.db, go, {'':6, 'Polymorphic':3}.get(select_type, 4))
|
|
|
|
sess = create_session()
|
|
|
|
# assert the JOINs dont over JOIN
|
|
assert sess.query(Person).with_polymorphic('*').options(eagerload(Engineer.machines)).limit(2).offset(1).with_labels().subquery().count().scalar() == 2
|
|
|
|
def go():
|
|
self.assertEquals(sess.query(Person).with_polymorphic('*').options(eagerload(Engineer.machines))[1:3], all_employees[1:3])
|
|
self.assert_sql_count(testing.db, go, 3)
|
|
|
|
|
|
def test_get(self):
|
|
sess = create_session()
|
|
|
|
# for all mappers, ensure the primary key has been calculated as just the "person_id"
|
|
# column
|
|
self.assertEquals(sess.query(Person).get(e1.person_id), Engineer(name="dilbert", primary_language="java"))
|
|
self.assertEquals(sess.query(Engineer).get(e1.person_id), Engineer(name="dilbert", primary_language="java"))
|
|
self.assertEquals(sess.query(Manager).get(b1.person_id), Boss(name="pointy haired boss", golf_swing="fore"))
|
|
|
|
def test_filter_on_subclass(self):
|
|
sess = create_session()
|
|
self.assertEquals(sess.query(Engineer).all()[0], Engineer(name="dilbert"))
|
|
|
|
self.assertEquals(sess.query(Engineer).first(), Engineer(name="dilbert"))
|
|
|
|
self.assertEquals(sess.query(Engineer).filter(Engineer.person_id==e1.person_id).first(), Engineer(name="dilbert"))
|
|
|
|
self.assertEquals(sess.query(Manager).filter(Manager.person_id==m1.person_id).one(), Manager(name="dogbert"))
|
|
|
|
self.assertEquals(sess.query(Manager).filter(Manager.person_id==b1.person_id).one(), Boss(name="pointy haired boss"))
|
|
|
|
self.assertEquals(sess.query(Boss).filter(Boss.person_id==b1.person_id).one(), Boss(name="pointy haired boss"))
|
|
|
|
def test_join_from_polymorphic(self):
|
|
sess = create_session()
|
|
|
|
for aliased in (True, False):
|
|
self.assertEquals(sess.query(Person).join('paperwork', aliased=aliased).filter(Paperwork.description.like('%review%')).all(), [b1, m1])
|
|
|
|
self.assertEquals(sess.query(Person).join('paperwork', aliased=aliased).filter(Paperwork.description.like('%#2%')).all(), [e1, m1])
|
|
|
|
self.assertEquals(sess.query(Engineer).join('paperwork', aliased=aliased).filter(Paperwork.description.like('%#2%')).all(), [e1])
|
|
|
|
self.assertEquals(sess.query(Person).join('paperwork', aliased=aliased).filter(Person.name.like('%dog%')).filter(Paperwork.description.like('%#2%')).all(), [m1])
|
|
|
|
def test_join_from_with_polymorphic(self):
|
|
sess = create_session()
|
|
|
|
for aliased in (True, False):
|
|
sess.clear()
|
|
self.assertEquals(sess.query(Person).with_polymorphic(Manager).join('paperwork', aliased=aliased).filter(Paperwork.description.like('%review%')).all(), [b1, m1])
|
|
|
|
sess.clear()
|
|
self.assertEquals(sess.query(Person).with_polymorphic([Manager, Engineer]).join('paperwork', aliased=aliased).filter(Paperwork.description.like('%#2%')).all(), [e1, m1])
|
|
|
|
sess.clear()
|
|
self.assertEquals(sess.query(Person).with_polymorphic([Manager, Engineer]).join('paperwork', aliased=aliased).filter(Person.name.like('%dog%')).filter(Paperwork.description.like('%#2%')).all(), [m1])
|
|
|
|
def test_join_to_polymorphic(self):
|
|
sess = create_session()
|
|
self.assertEquals(sess.query(Company).join('employees').filter(Person.name=='vlad').one(), c2)
|
|
|
|
self.assertEquals(sess.query(Company).join('employees', aliased=True).filter(Person.name=='vlad').one(), c2)
|
|
|
|
def test_polymorphic_any(self):
|
|
sess = create_session()
|
|
|
|
self.assertEquals(
|
|
sess.query(Company).filter(Company.employees.of_type(Engineer).any(Engineer.primary_language=='cobol')).one(),
|
|
c2
|
|
)
|
|
|
|
self.assertEquals(
|
|
sess.query(Company).filter(Company.employees.of_type(Boss).any(Boss.golf_swing=='fore')).one(),
|
|
c1
|
|
)
|
|
self.assertEquals(
|
|
sess.query(Company).filter(Company.employees.of_type(Boss).any(Manager.manager_name=='pointy')).one(),
|
|
c1
|
|
)
|
|
|
|
if select_type != '':
|
|
self.assertEquals(
|
|
sess.query(Person).filter(Engineer.machines.any(Machine.name=="Commodore 64")).all(), [e2, e3]
|
|
)
|
|
|
|
self.assertEquals(
|
|
sess.query(Person).filter(Person.paperwork.any(Paperwork.description=="review #2")).all(), [m1]
|
|
)
|
|
|
|
self.assertEquals(
|
|
sess.query(Company).filter(Company.employees.of_type(Engineer).any(and_(Engineer.primary_language=='cobol'))).one(),
|
|
c2
|
|
)
|
|
|
|
|
|
def test_expire(self):
|
|
"""test that individual column refresh doesn't get tripped up by the select_table mapper"""
|
|
|
|
sess = create_session()
|
|
m1 = sess.query(Manager).filter(Manager.name=='dogbert').one()
|
|
sess.expire(m1)
|
|
assert m1.status == 'regular manager'
|
|
|
|
m2 = sess.query(Manager).filter(Manager.name=='pointy haired boss').one()
|
|
sess.expire(m2, ['manager_name', 'golf_swing'])
|
|
assert m2.golf_swing=='fore'
|
|
|
|
def test_with_polymorphic(self):
|
|
|
|
sess = create_session()
|
|
|
|
|
|
self.assertRaises(sa_exc.InvalidRequestError, sess.query(Person).with_polymorphic, Paperwork)
|
|
self.assertRaises(sa_exc.InvalidRequestError, sess.query(Engineer).with_polymorphic, Boss)
|
|
self.assertRaises(sa_exc.InvalidRequestError, sess.query(Engineer).with_polymorphic, Person)
|
|
|
|
# compare to entities without related collections to prevent additional lazy SQL from firing on
|
|
# loaded entities
|
|
emps_without_relations = [
|
|
Engineer(name="dilbert", engineer_name="dilbert", primary_language="java", status="regular engineer"),
|
|
Engineer(name="wally", engineer_name="wally", primary_language="c++", status="regular engineer"),
|
|
Boss(name="pointy haired boss", golf_swing="fore", manager_name="pointy", status="da boss"),
|
|
Manager(name="dogbert", manager_name="dogbert", status="regular manager"),
|
|
Engineer(name="vlad", engineer_name="vlad", primary_language="cobol", status="elbonian engineer")
|
|
]
|
|
self.assertEquals(sess.query(Person).with_polymorphic('*').all(), emps_without_relations)
|
|
|
|
|
|
def go():
|
|
self.assertEquals(sess.query(Person).with_polymorphic(Engineer).filter(Engineer.primary_language=='java').all(), emps_without_relations[0:1])
|
|
self.assert_sql_count(testing.db, go, 1)
|
|
|
|
sess.clear()
|
|
def go():
|
|
self.assertEquals(sess.query(Person).with_polymorphic('*').all(), emps_without_relations)
|
|
self.assert_sql_count(testing.db, go, 1)
|
|
|
|
sess.clear()
|
|
def go():
|
|
self.assertEquals(sess.query(Person).with_polymorphic(Engineer).all(), emps_without_relations)
|
|
self.assert_sql_count(testing.db, go, 3)
|
|
|
|
sess.clear()
|
|
def go():
|
|
self.assertEquals(sess.query(Person).with_polymorphic(Engineer, people.outerjoin(engineers)).all(), emps_without_relations)
|
|
self.assert_sql_count(testing.db, go, 3)
|
|
|
|
sess.clear()
|
|
def go():
|
|
# limit the polymorphic join down to just "Person", overriding select_table
|
|
self.assertEquals(sess.query(Person).with_polymorphic(Person).all(), emps_without_relations)
|
|
self.assert_sql_count(testing.db, go, 6)
|
|
|
|
def test_relation_to_polymorphic(self):
|
|
assert_result = [
|
|
Company(name="MegaCorp, Inc.", employees=[
|
|
Engineer(name="dilbert", engineer_name="dilbert", primary_language="java", status="regular engineer", machines=[Machine(name="IBM ThinkPad"), Machine(name="IPhone")]),
|
|
Engineer(name="wally", engineer_name="wally", primary_language="c++", status="regular engineer"),
|
|
Boss(name="pointy haired boss", golf_swing="fore", manager_name="pointy", status="da boss"),
|
|
Manager(name="dogbert", manager_name="dogbert", status="regular manager"),
|
|
]),
|
|
Company(name="Elbonia, Inc.", employees=[
|
|
Engineer(name="vlad", engineer_name="vlad", primary_language="cobol", status="elbonian engineer")
|
|
])
|
|
]
|
|
|
|
sess = create_session()
|
|
|
|
def go():
|
|
# test load Companies with lazy load to 'employees'
|
|
self.assertEquals(sess.query(Company).all(), assert_result)
|
|
self.assert_sql_count(testing.db, go, {'':9, 'Polymorphic':4}.get(select_type, 5))
|
|
|
|
sess = create_session()
|
|
def go():
|
|
# currently, it doesn't matter if we say Company.employees, or Company.employees.of_type(Engineer). eagerloader doesn't
|
|
# pick up on the "of_type()" as of yet.
|
|
self.assertEquals(sess.query(Company).options(eagerload_all([Company.employees.of_type(Engineer), Engineer.machines])).all(), assert_result)
|
|
|
|
# in the case of select_type='', the eagerload doesn't take in this case;
|
|
# it eagerloads company->people, then a load for each of 5 rows, then lazyload of "machines"
|
|
self.assert_sql_count(testing.db, go, {'':7, 'Polymorphic':1}.get(select_type, 2))
|
|
|
|
def test_eagerload_on_subclass(self):
|
|
sess = create_session()
|
|
def go():
|
|
# test load People with eagerload to engineers + machines
|
|
self.assertEquals(sess.query(Person).with_polymorphic('*').options(eagerload([Engineer.machines])).filter(Person.name=='dilbert').all(),
|
|
[Engineer(name="dilbert", engineer_name="dilbert", primary_language="java", status="regular engineer", machines=[Machine(name="IBM ThinkPad"), Machine(name="IPhone")])]
|
|
)
|
|
self.assert_sql_count(testing.db, go, 1)
|
|
|
|
def test_join_to_subclass(self):
|
|
sess = create_session()
|
|
self.assertEquals(sess.query(Company).join(('employees', people.join(engineers))).filter(Engineer.primary_language=='java').all(), [c1])
|
|
|
|
if select_type == '':
|
|
self.assertEquals(sess.query(Company).select_from(companies.join(people).join(engineers)).filter(Engineer.primary_language=='java').all(), [c1])
|
|
self.assertEquals(sess.query(Company).join(('employees', people.join(engineers))).filter(Engineer.primary_language=='java').all(), [c1])
|
|
|
|
ealias = aliased(Engineer)
|
|
self.assertEquals(sess.query(Company).join(('employees', ealias)).filter(ealias.primary_language=='java').all(), [c1])
|
|
|
|
self.assertEquals(sess.query(Person).select_from(people.join(engineers)).join(Engineer.machines).all(), [e1, e2, e3])
|
|
self.assertEquals(sess.query(Person).select_from(people.join(engineers)).join(Engineer.machines).filter(Machine.name.ilike("%ibm%")).all(), [e1, e3])
|
|
self.assertEquals(sess.query(Company).join([('employees', people.join(engineers)), Engineer.machines]).all(), [c1, c2])
|
|
self.assertEquals(sess.query(Company).join([('employees', people.join(engineers)), Engineer.machines]).filter(Machine.name.ilike("%thinkpad%")).all(), [c1])
|
|
else:
|
|
self.assertEquals(sess.query(Company).select_from(companies.join(people).join(engineers)).filter(Engineer.primary_language=='java').all(), [c1])
|
|
self.assertEquals(sess.query(Company).join(['employees']).filter(Engineer.primary_language=='java').all(), [c1])
|
|
self.assertEquals(sess.query(Person).join(Engineer.machines).all(), [e1, e2, e3])
|
|
self.assertEquals(sess.query(Person).join(Engineer.machines).filter(Machine.name.ilike("%ibm%")).all(), [e1, e3])
|
|
self.assertEquals(sess.query(Company).join(['employees', Engineer.machines]).all(), [c1, c2])
|
|
self.assertEquals(sess.query(Company).join(['employees', Engineer.machines]).filter(Machine.name.ilike("%thinkpad%")).all(), [c1])
|
|
|
|
# non-polymorphic
|
|
self.assertEquals(sess.query(Engineer).join(Engineer.machines).all(), [e1, e2, e3])
|
|
self.assertEquals(sess.query(Engineer).join(Engineer.machines).filter(Machine.name.ilike("%ibm%")).all(), [e1, e3])
|
|
|
|
# here's the new way
|
|
self.assertEquals(sess.query(Company).join(Company.employees.of_type(Engineer)).filter(Engineer.primary_language=='java').all(), [c1])
|
|
self.assertEquals(sess.query(Company).join([Company.employees.of_type(Engineer), 'machines']).filter(Machine.name.ilike("%thinkpad%")).all(), [c1])
|
|
|
|
def test_join_through_polymorphic(self):
|
|
|
|
sess = create_session()
|
|
|
|
for aliased in (True, False):
|
|
self.assertEquals(
|
|
sess.query(Company).\
|
|
join(['employees', 'paperwork'], aliased=aliased).filter(Paperwork.description.like('%#2%')).all(),
|
|
[c1]
|
|
)
|
|
|
|
self.assertEquals(
|
|
sess.query(Company).\
|
|
join(['employees', 'paperwork'], aliased=aliased).filter(Paperwork.description.like('%#%')).all(),
|
|
[c1, c2]
|
|
)
|
|
|
|
self.assertEquals(
|
|
sess.query(Company).\
|
|
join(['employees', 'paperwork'], aliased=aliased).filter(Person.name.in_(['dilbert', 'vlad'])).filter(Paperwork.description.like('%#2%')).all(),
|
|
[c1]
|
|
)
|
|
|
|
self.assertEquals(
|
|
sess.query(Company).\
|
|
join(['employees', 'paperwork'], aliased=aliased).filter(Person.name.in_(['dilbert', 'vlad'])).filter(Paperwork.description.like('%#%')).all(),
|
|
[c1, c2]
|
|
)
|
|
|
|
self.assertEquals(
|
|
sess.query(Company).join('employees', aliased=aliased).filter(Person.name.in_(['dilbert', 'vlad'])).\
|
|
join('paperwork', from_joinpoint=True, aliased=aliased).filter(Paperwork.description.like('%#2%')).all(),
|
|
[c1]
|
|
)
|
|
|
|
self.assertEquals(
|
|
sess.query(Company).join('employees', aliased=aliased).filter(Person.name.in_(['dilbert', 'vlad'])).\
|
|
join('paperwork', from_joinpoint=True, aliased=aliased).filter(Paperwork.description.like('%#%')).all(),
|
|
[c1, c2]
|
|
)
|
|
|
|
def test_filter_on_baseclass(self):
|
|
sess = create_session()
|
|
|
|
self.assertEquals(sess.query(Person).all(), all_employees)
|
|
|
|
self.assertEquals(sess.query(Person).first(), all_employees[0])
|
|
|
|
self.assertEquals(sess.query(Person).filter(Person.person_id==e2.person_id).one(), e2)
|
|
|
|
def test_from_alias(self):
|
|
sess = create_session()
|
|
|
|
palias = aliased(Person)
|
|
self.assertEquals(
|
|
sess.query(palias).filter(palias.name.in_(['dilbert', 'wally'])).all(),
|
|
[e1, e2]
|
|
)
|
|
|
|
def test_self_referential(self):
|
|
sess = create_session()
|
|
|
|
c1_employees = [e1, e2, b1, m1]
|
|
|
|
palias = aliased(Person)
|
|
self.assertEquals(
|
|
sess.query(Person, palias).filter(Person.company_id==palias.company_id).filter(Person.name=='dogbert').\
|
|
filter(Person.person_id>palias.person_id).order_by(Person.person_id, palias.person_id).all(),
|
|
[
|
|
(m1, e1),
|
|
(m1, e2),
|
|
(m1, b1),
|
|
]
|
|
)
|
|
|
|
self.assertEquals(
|
|
sess.query(Person, palias).filter(Person.company_id==palias.company_id).filter(Person.name=='dogbert').\
|
|
filter(Person.person_id>palias.person_id).from_self().order_by(Person.person_id, palias.person_id).all(),
|
|
[
|
|
(m1, e1),
|
|
(m1, e2),
|
|
(m1, b1),
|
|
]
|
|
)
|
|
|
|
def test_nesting_queries(self):
|
|
sess = create_session()
|
|
|
|
# query.statement places a flag "no_adapt" on the returned statement. This prevents
|
|
# the polymorphic adaptation in the second "filter" from hitting it, which would pollute
|
|
# the subquery and usually results in recursion overflow errors within the adaption.
|
|
subq = sess.query(engineers.c.person_id).filter(Engineer.primary_language=='java').statement.as_scalar()
|
|
|
|
self.assertEquals(sess.query(Person).filter(Person.person_id==subq).one(), e1)
|
|
|
|
def test_mixed_entities(self):
|
|
sess = create_session()
|
|
|
|
self.assertEquals(
|
|
sess.query(Company.name, Person).join(Company.employees).filter(Company.name=='Elbonia, Inc.').all(),
|
|
[(u'Elbonia, Inc.',
|
|
Engineer(status=u'elbonian engineer',engineer_name=u'vlad',name=u'vlad',primary_language=u'cobol'))]
|
|
)
|
|
|
|
self.assertEquals(
|
|
sess.query(Person, Company.name).join(Company.employees).filter(Company.name=='Elbonia, Inc.').all(),
|
|
[(Engineer(status=u'elbonian engineer',engineer_name=u'vlad',name=u'vlad',primary_language=u'cobol'),
|
|
u'Elbonia, Inc.')]
|
|
)
|
|
|
|
|
|
self.assertEquals(
|
|
sess.query(Manager.name).all(),
|
|
[('pointy haired boss', ), ('dogbert',)]
|
|
)
|
|
|
|
self.assertEquals(
|
|
sess.query(Manager.name + " foo").all(),
|
|
[('pointy haired boss foo', ), ('dogbert foo',)]
|
|
)
|
|
|
|
row = sess.query(Engineer.name, Engineer.primary_language).filter(Engineer.name=='dilbert').first()
|
|
assert row.name == 'dilbert'
|
|
assert row.primary_language == 'java'
|
|
|
|
|
|
self.assertEquals(
|
|
sess.query(Engineer.name, Engineer.primary_language).all(),
|
|
[(u'dilbert', u'java'), (u'wally', u'c++'), (u'vlad', u'cobol')]
|
|
)
|
|
|
|
self.assertEquals(
|
|
sess.query(Boss.name, Boss.golf_swing).all(),
|
|
[(u'pointy haired boss', u'fore')]
|
|
)
|
|
|
|
# TODO: I think raise error on these for now. different inheritance/loading schemes have different
|
|
# results here, all incorrect
|
|
#
|
|
# self.assertEquals(
|
|
# sess.query(Person.name, Engineer.primary_language).all(),
|
|
# []
|
|
# )
|
|
|
|
# self.assertEquals(
|
|
# sess.query(Person.name, Engineer.primary_language, Manager.manager_name).all(),
|
|
# []
|
|
# )
|
|
|
|
self.assertEquals(
|
|
sess.query(Person.name, Company.name).join(Company.employees).filter(Company.name=='Elbonia, Inc.').all(),
|
|
[(u'vlad',u'Elbonia, Inc.')]
|
|
)
|
|
|
|
self.assertEquals(
|
|
sess.query(Engineer.primary_language).filter(Person.type=='engineer').all(),
|
|
[(u'java',), (u'c++',), (u'cobol',)]
|
|
)
|
|
|
|
if select_type != '':
|
|
self.assertEquals(
|
|
sess.query(Engineer, Company.name).join(Company.employees).filter(Person.type=='engineer').all(),
|
|
[
|
|
(Engineer(status=u'regular engineer',engineer_name=u'dilbert',name=u'dilbert',company_id=1,primary_language=u'java',person_id=1,type=u'engineer'), u'MegaCorp, Inc.'),
|
|
(Engineer(status=u'regular engineer',engineer_name=u'wally',name=u'wally',company_id=1,primary_language=u'c++',person_id=2,type=u'engineer'), u'MegaCorp, Inc.'),
|
|
(Engineer(status=u'elbonian engineer',engineer_name=u'vlad',name=u'vlad',company_id=2,primary_language=u'cobol',person_id=5,type=u'engineer'), u'Elbonia, Inc.')
|
|
]
|
|
)
|
|
|
|
self.assertEquals(
|
|
sess.query(Engineer.primary_language, Company.name).join(Company.employees).filter(Person.type=='engineer').order_by(desc(Engineer.primary_language)).all(),
|
|
[(u'java', u'MegaCorp, Inc.'), (u'cobol', u'Elbonia, Inc.'), (u'c++', u'MegaCorp, Inc.')]
|
|
)
|
|
|
|
palias = aliased(Person)
|
|
self.assertEquals(
|
|
sess.query(Person, Company.name, palias).join(Company.employees).filter(Company.name=='Elbonia, Inc.').filter(palias.name=='dilbert').all(),
|
|
[(Engineer(status=u'elbonian engineer',engineer_name=u'vlad',name=u'vlad',primary_language=u'cobol'),
|
|
u'Elbonia, Inc.',
|
|
Engineer(status=u'regular engineer',engineer_name=u'dilbert',name=u'dilbert',company_id=1,primary_language=u'java',person_id=1,type=u'engineer'))]
|
|
)
|
|
|
|
self.assertEquals(
|
|
sess.query(palias, Company.name, Person).join(Company.employees).filter(Company.name=='Elbonia, Inc.').filter(palias.name=='dilbert').all(),
|
|
[(Engineer(status=u'regular engineer',engineer_name=u'dilbert',name=u'dilbert',company_id=1,primary_language=u'java',person_id=1,type=u'engineer'),
|
|
u'Elbonia, Inc.',
|
|
Engineer(status=u'elbonian engineer',engineer_name=u'vlad',name=u'vlad',primary_language=u'cobol'),)
|
|
]
|
|
)
|
|
|
|
self.assertEquals(
|
|
sess.query(Person.name, Company.name, palias.name).join(Company.employees).filter(Company.name=='Elbonia, Inc.').filter(palias.name=='dilbert').all(),
|
|
[(u'vlad', u'Elbonia, Inc.', u'dilbert')]
|
|
)
|
|
|
|
palias = aliased(Person)
|
|
self.assertEquals(
|
|
sess.query(Person.type, Person.name, palias.type, palias.name).filter(Person.company_id==palias.company_id).filter(Person.name=='dogbert').\
|
|
filter(Person.person_id>palias.person_id).order_by(Person.person_id, palias.person_id).all(),
|
|
[(u'manager', u'dogbert', u'engineer', u'dilbert'),
|
|
(u'manager', u'dogbert', u'engineer', u'wally'),
|
|
(u'manager', u'dogbert', u'boss', u'pointy haired boss')]
|
|
)
|
|
|
|
self.assertEquals(
|
|
sess.query(Person.name, Paperwork.description).filter(Person.person_id==Paperwork.person_id).order_by(Person.name, Paperwork.description).all(),
|
|
[(u'dilbert', u'tps report #1'), (u'dilbert', u'tps report #2'), (u'dogbert', u'review #2'),
|
|
(u'dogbert', u'review #3'),
|
|
(u'pointy haired boss', u'review #1'),
|
|
(u'vlad', u'elbonian missive #3'),
|
|
(u'wally', u'tps report #3'),
|
|
(u'wally', u'tps report #4'),
|
|
]
|
|
)
|
|
|
|
if select_type != '':
|
|
self.assertEquals(
|
|
sess.query(func.count(Person.person_id)).filter(Engineer.primary_language=='java').all(),
|
|
[(1, )]
|
|
)
|
|
|
|
self.assertEquals(
|
|
sess.query(Company.name, func.count(Person.person_id)).filter(Company.company_id==Person.company_id).group_by(Company.name).order_by(Company.name).all(),
|
|
[(u'Elbonia, Inc.', 1), (u'MegaCorp, Inc.', 4)]
|
|
)
|
|
|
|
self.assertEquals(
|
|
sess.query(Company.name, func.count(Person.person_id)).join(Company.employees).group_by(Company.name).order_by(Company.name).all(),
|
|
[(u'Elbonia, Inc.', 1), (u'MegaCorp, Inc.', 4)]
|
|
)
|
|
|
|
|
|
PolymorphicQueryTest.__name__ = "Polymorphic%sTest" % select_type
|
|
return PolymorphicQueryTest
|
|
|
|
for select_type in ('', 'Polymorphic', 'Unions', 'AliasedJoins', 'Joins'):
|
|
testclass = make_test(select_type)
|
|
exec("%s = testclass" % testclass.__name__)
|
|
|
|
del testclass
|
|
|
|
class SelfReferentialTestJoinedToBase(ORMTest):
|
|
keep_mappers = True
|
|
|
|
def define_tables(self, metadata):
|
|
global people, engineers
|
|
people = Table('people', metadata,
|
|
Column('person_id', Integer, Sequence('person_id_seq', optional=True), primary_key=True),
|
|
Column('name', String(50)),
|
|
Column('type', String(30)))
|
|
|
|
engineers = Table('engineers', metadata,
|
|
Column('person_id', Integer, ForeignKey('people.person_id'), primary_key=True),
|
|
Column('primary_language', String(50)),
|
|
Column('reports_to_id', Integer, ForeignKey('people.person_id'))
|
|
)
|
|
|
|
mapper(Person, people, polymorphic_on=people.c.type, polymorphic_identity='person')
|
|
mapper(Engineer, engineers, inherits=Person,
|
|
inherit_condition=engineers.c.person_id==people.c.person_id,
|
|
polymorphic_identity='engineer', properties={
|
|
'reports_to':relation(Person, primaryjoin=people.c.person_id==engineers.c.reports_to_id)
|
|
})
|
|
|
|
def test_has(self):
|
|
|
|
p1 = Person(name='dogbert')
|
|
e1 = Engineer(name='dilbert', primary_language='java', reports_to=p1)
|
|
sess = create_session()
|
|
sess.save(p1)
|
|
sess.save(e1)
|
|
sess.flush()
|
|
sess.clear()
|
|
|
|
self.assertEquals(sess.query(Engineer).filter(Engineer.reports_to.has(Person.name=='dogbert')).first(), Engineer(name='dilbert'))
|
|
|
|
def test_oftype_aliases_in_exists(self):
|
|
e1 = Engineer(name='dilbert', primary_language='java')
|
|
e2 = Engineer(name='wally', primary_language='c++', reports_to=e1)
|
|
sess = create_session()
|
|
sess.add_all([e1, e2])
|
|
sess.flush()
|
|
|
|
self.assertEquals(sess.query(Engineer).filter(Engineer.reports_to.of_type(Engineer).has(Engineer.name=='dilbert')).first(), e2)
|
|
|
|
def test_join(self):
|
|
p1 = Person(name='dogbert')
|
|
e1 = Engineer(name='dilbert', primary_language='java', reports_to=p1)
|
|
sess = create_session()
|
|
sess.save(p1)
|
|
sess.save(e1)
|
|
sess.flush()
|
|
sess.clear()
|
|
|
|
self.assertEquals(
|
|
sess.query(Engineer).join('reports_to', aliased=True).filter(Person.name=='dogbert').first(),
|
|
Engineer(name='dilbert'))
|
|
|
|
class SelfReferentialTestJoinedToJoined(ORMTest):
|
|
keep_mappers = True
|
|
|
|
def define_tables(self, metadata):
|
|
global people, engineers
|
|
people = Table('people', metadata,
|
|
Column('person_id', Integer, Sequence('person_id_seq', optional=True), primary_key=True),
|
|
Column('name', String(50)),
|
|
Column('type', String(30)))
|
|
|
|
engineers = Table('engineers', metadata,
|
|
Column('person_id', Integer, ForeignKey('people.person_id'), primary_key=True),
|
|
Column('primary_language', String(50)),
|
|
Column('reports_to_id', Integer, ForeignKey('managers.person_id'))
|
|
)
|
|
|
|
managers = Table('managers', metadata,
|
|
Column('person_id', Integer, ForeignKey('people.person_id'), primary_key=True),
|
|
)
|
|
|
|
mapper(Person, people, polymorphic_on=people.c.type, polymorphic_identity='person')
|
|
mapper(Manager, managers, inherits=Person, polymorphic_identity='manager')
|
|
|
|
mapper(Engineer, engineers, inherits=Person,
|
|
polymorphic_identity='engineer', properties={
|
|
'reports_to':relation(Manager, primaryjoin=managers.c.person_id==engineers.c.reports_to_id)
|
|
})
|
|
|
|
def test_has(self):
|
|
|
|
m1 = Manager(name='dogbert')
|
|
e1 = Engineer(name='dilbert', primary_language='java', reports_to=m1)
|
|
sess = create_session()
|
|
sess.save(m1)
|
|
sess.save(e1)
|
|
sess.flush()
|
|
sess.clear()
|
|
|
|
self.assertEquals(sess.query(Engineer).filter(Engineer.reports_to.has(Manager.name=='dogbert')).first(), Engineer(name='dilbert'))
|
|
|
|
def test_join(self):
|
|
m1 = Manager(name='dogbert')
|
|
e1 = Engineer(name='dilbert', primary_language='java', reports_to=m1)
|
|
sess = create_session()
|
|
sess.save(m1)
|
|
sess.save(e1)
|
|
sess.flush()
|
|
sess.clear()
|
|
|
|
self.assertEquals(
|
|
sess.query(Engineer).join('reports_to', aliased=True).filter(Manager.name=='dogbert').first(),
|
|
Engineer(name='dilbert'))
|
|
|
|
|
|
class M2MFilterTest(ORMTest):
|
|
keep_mappers = True
|
|
keep_data = True
|
|
|
|
def define_tables(self, metadata):
|
|
global people, engineers, Organization
|
|
|
|
organizations = Table('organizations', metadata,
|
|
Column('id', Integer, Sequence('org_id_seq', optional=True), primary_key=True),
|
|
Column('name', String(50)),
|
|
)
|
|
engineers_to_org = Table('engineers_org', metadata,
|
|
Column('org_id', Integer, ForeignKey('organizations.id')),
|
|
Column('engineer_id', Integer, ForeignKey('engineers.person_id')),
|
|
)
|
|
|
|
people = Table('people', metadata,
|
|
Column('person_id', Integer, Sequence('person_id_seq', optional=True), primary_key=True),
|
|
Column('name', String(50)),
|
|
Column('type', String(30)))
|
|
|
|
engineers = Table('engineers', metadata,
|
|
Column('person_id', Integer, ForeignKey('people.person_id'), primary_key=True),
|
|
Column('primary_language', String(50)),
|
|
)
|
|
|
|
class Organization(fixtures.Base):
|
|
pass
|
|
|
|
mapper(Organization, organizations, properties={
|
|
'engineers':relation(Engineer, secondary=engineers_to_org, backref='organizations')
|
|
})
|
|
|
|
mapper(Person, people, polymorphic_on=people.c.type, polymorphic_identity='person')
|
|
mapper(Engineer, engineers, inherits=Person, polymorphic_identity='engineer')
|
|
|
|
def insert_data(self):
|
|
e1 = Engineer(name='e1')
|
|
e2 = Engineer(name='e2')
|
|
e3 = Engineer(name='e3')
|
|
e4 = Engineer(name='e4')
|
|
org1 = Organization(name='org1', engineers=[e1, e2])
|
|
org2 = Organization(name='org2', engineers=[e3, e4])
|
|
|
|
sess = create_session()
|
|
sess.save(org1)
|
|
sess.save(org2)
|
|
sess.flush()
|
|
|
|
def test_not_contains(self):
|
|
sess = create_session()
|
|
|
|
e1 = sess.query(Person).filter(Engineer.name=='e1').one()
|
|
|
|
# this works
|
|
self.assertEquals(sess.query(Organization).filter(~Organization.engineers.of_type(Engineer).contains(e1)).all(), [Organization(name='org2')])
|
|
|
|
# this had a bug
|
|
self.assertEquals(sess.query(Organization).filter(~Organization.engineers.contains(e1)).all(), [Organization(name='org2')])
|
|
|
|
def test_any(self):
|
|
sess = create_session()
|
|
self.assertEquals(sess.query(Organization).filter(Organization.engineers.of_type(Engineer).any(Engineer.name=='e1')).all(), [Organization(name='org1')])
|
|
self.assertEquals(sess.query(Organization).filter(Organization.engineers.any(Engineer.name=='e1')).all(), [Organization(name='org1')])
|
|
|
|
class SelfReferentialM2MTest(ORMTest, AssertsCompiledSQL):
|
|
def define_tables(self, metadata):
|
|
Base = declarative_base(metadata=metadata)
|
|
|
|
secondary_table = Table('secondary', Base.metadata,
|
|
Column('left_id', Integer, ForeignKey('parent.id'), nullable=False),
|
|
Column('right_id', Integer, ForeignKey('parent.id'), nullable=False))
|
|
|
|
global Parent, Child1, Child2
|
|
class Parent(Base):
|
|
__tablename__ = 'parent'
|
|
id = Column(Integer, primary_key=True)
|
|
cls = Column(String(50))
|
|
__mapper_args__ = dict(polymorphic_on = cls )
|
|
|
|
class Child1(Parent):
|
|
__tablename__ = 'child1'
|
|
id = Column(Integer, ForeignKey('parent.id'), primary_key=True)
|
|
__mapper_args__ = dict(polymorphic_identity = 'child1')
|
|
|
|
class Child2(Parent):
|
|
__tablename__ = 'child2'
|
|
id = Column(Integer, ForeignKey('parent.id'), primary_key=True)
|
|
__mapper_args__ = dict(polymorphic_identity = 'child2')
|
|
|
|
Child1.left_child2 = relation(Child2, secondary = secondary_table,
|
|
primaryjoin = Parent.id == secondary_table.c.right_id,
|
|
secondaryjoin = Parent.id == secondary_table.c.left_id,
|
|
uselist = False,
|
|
)
|
|
|
|
def test_eager_join(self):
|
|
session = create_session()
|
|
|
|
c1 = Child1()
|
|
c1.left_child2 = Child2()
|
|
session.add(c1)
|
|
session.flush()
|
|
|
|
q = session.query(Child1).options(eagerload('left_child2'))
|
|
|
|
# test that the splicing of the join works here, doesnt break in the middle of "parent join child1"
|
|
self.assert_compile(q.limit(1).with_labels().statement,
|
|
"SELECT anon_1.parent_id AS anon_1_parent_id, anon_1.child1_id AS anon_1_child1_id, "\
|
|
"anon_1.parent_cls AS anon_1_parent_cls, anon_2.parent_id AS anon_2_parent_id, "\
|
|
"anon_2.child2_id AS anon_2_child2_id, anon_2.parent_cls AS anon_2_parent_cls FROM "\
|
|
"(SELECT parent.id AS parent_id, child1.id AS child1_id, parent.cls AS parent_cls FROM parent "\
|
|
"JOIN child1 ON parent.id = child1.id LIMIT 1) AS anon_1 LEFT OUTER JOIN secondary AS secondary_1 "\
|
|
"ON anon_1.parent_id = secondary_1.right_id LEFT OUTER JOIN (SELECT parent.id AS parent_id, "\
|
|
"parent.cls AS parent_cls, child2.id AS child2_id FROM parent JOIN child2 ON parent.id = child2.id) "\
|
|
"AS anon_2 ON anon_2.parent_id = secondary_1.left_id"
|
|
, dialect=default.DefaultDialect())
|
|
|
|
# another way to check
|
|
assert q.limit(1).with_labels().subquery().count().scalar() == 1
|
|
|
|
assert q.first() is c1
|
|
|
|
if __name__ == "__main__":
|
|
testenv.main()
|