diff options
author | jonathan vanasco <jonathan@2xlp.com> | 2015-04-02 13:30:26 -0400 |
---|---|---|
committer | jonathan vanasco <jonathan@2xlp.com> | 2015-04-02 13:30:26 -0400 |
commit | 6de3d490a2adb0fff43f98e15a53407b46668b61 (patch) | |
tree | d5e0e2077dfe7dc69ce30e9d0a8c89ceff78e3fe /test/ext | |
parent | efca4af93603faa7abfeacbab264cad85ee4105c (diff) | |
parent | 5e04995a82c00e801a99765cde7726f5e73e18c2 (diff) | |
download | sqlalchemy-6de3d490a2adb0fff43f98e15a53407b46668b61.tar.gz |
Merge branch 'master' of bitbucket.org:zzzeek/sqlalchemy
Diffstat (limited to 'test/ext')
-rw-r--r-- | test/ext/declarative/test_inheritance.py | 35 | ||||
-rw-r--r-- | test/ext/declarative/test_mixin.py | 127 | ||||
-rw-r--r-- | test/ext/test_associationproxy.py | 16 | ||||
-rw-r--r-- | test/ext/test_baked.py | 768 | ||||
-rw-r--r-- | test/ext/test_extendedattr.py | 2 | ||||
-rw-r--r-- | test/ext/test_horizontal_shard.py | 2 |
6 files changed, 947 insertions, 3 deletions
diff --git a/test/ext/declarative/test_inheritance.py b/test/ext/declarative/test_inheritance.py index 6ea37e4d3..2ecee99fd 100644 --- a/test/ext/declarative/test_inheritance.py +++ b/test/ext/declarative/test_inheritance.py @@ -485,6 +485,41 @@ class DeclarativeInheritanceTest(DeclarativeTestBase): ).one(), Engineer(name='vlad', primary_language='cobol')) + def test_single_constraint_on_sub(self): + """test the somewhat unusual case of [ticket:3341]""" + + class Person(Base, fixtures.ComparableEntity): + + __tablename__ = 'people' + id = Column(Integer, primary_key=True, + test_needs_autoincrement=True) + name = Column(String(50)) + discriminator = Column('type', String(50)) + __mapper_args__ = {'polymorphic_on': discriminator} + + class Engineer(Person): + + __mapper_args__ = {'polymorphic_identity': 'engineer'} + primary_language = Column(String(50)) + + __hack_args_one__ = sa.UniqueConstraint( + Person.name, primary_language) + __hack_args_two__ = sa.CheckConstraint( + Person.name != primary_language) + + uq = [c for c in Person.__table__.constraints + if isinstance(c, sa.UniqueConstraint)][0] + ck = [c for c in Person.__table__.constraints + if isinstance(c, sa.CheckConstraint)][0] + eq_( + list(uq.columns), + [Person.__table__.c.name, Person.__table__.c.primary_language] + ) + eq_( + list(ck.columns), + [Person.__table__.c.name, Person.__table__.c.primary_language] + ) + @testing.skip_if(lambda: testing.against('oracle'), "Test has an empty insert in it at the moment") def test_columns_single_inheritance_conflict_resolution(self): diff --git a/test/ext/declarative/test_mixin.py b/test/ext/declarative/test_mixin.py index db86927a1..45b881671 100644 --- a/test/ext/declarative/test_mixin.py +++ b/test/ext/declarative/test_mixin.py @@ -1392,6 +1392,39 @@ class DeclaredAttrTest(DeclarativeTestBase, testing.AssertsCompiledSQL): getattr, Mixin, "my_prop" ) + def test_non_decl_access(self): + counter = mock.Mock() + + class Mixin(object): + @declared_attr + def __tablename__(cls): + counter(cls) + return "foo" + + class Foo(Mixin, Base): + id = Column(Integer, primary_key=True) + + @declared_attr + def x(cls): + cls.__tablename__ + + @declared_attr + def y(cls): + cls.__tablename__ + + eq_( + counter.mock_calls, + [mock.call(Foo)] + ) + + eq_(Foo.__tablename__, 'foo') + eq_(Foo.__tablename__, 'foo') + + eq_( + counter.mock_calls, + [mock.call(Foo), mock.call(Foo), mock.call(Foo)] + ) + def test_property_noncascade(self): counter = mock.Mock() @@ -1432,6 +1465,59 @@ class DeclaredAttrTest(DeclarativeTestBase, testing.AssertsCompiledSQL): eq_(counter.mock_calls, [mock.call(A), mock.call(B)]) + def test_col_prop_attrs_associated_w_class_for_mapper_args(self): + from sqlalchemy import Column + import collections + + asserted = collections.defaultdict(set) + + class Mixin(object): + @declared_attr.cascading + def my_attr(cls): + if decl.has_inherited_table(cls): + id = Column(ForeignKey('a.my_attr'), primary_key=True) + asserted['b'].add(id) + else: + id = Column(Integer, primary_key=True) + asserted['a'].add(id) + return id + + class A(Base, Mixin): + __tablename__ = 'a' + + @declared_attr + def __mapper_args__(cls): + asserted['a'].add(cls.my_attr) + return {} + + # here: + # 1. A is mapped. so A.my_attr is now the InstrumentedAttribute. + # 2. B wants to call my_attr also. Due to .cascading, it has been + # invoked specific to B, and is present in the dict_ that will + # be used when we map the class. But except for the + # special setattr() we do in _scan_attributes() in this case, would + # otherwise not been set on the class as anything from this call; + # the usual mechanics of calling it from the descriptor also do not + # work because A is fully mapped and because A set it up, is currently + # that non-expected InstrumentedAttribute and replaces the + # descriptor from being invoked. + + class B(A): + __tablename__ = 'b' + + @declared_attr + def __mapper_args__(cls): + asserted['b'].add(cls.my_attr) + return {} + + eq_( + asserted, + { + 'a': set([A.my_attr.property.columns[0]]), + 'b': set([B.my_attr.property.columns[0]]) + } + ) + def test_column_pre_map(self): counter = mock.Mock() @@ -1517,3 +1603,44 @@ class AbstractTest(DeclarativeTestBase): id = Column(Integer, primary_key=True) eq_(set(Base.metadata.tables), set(['y', 'z', 'q'])) + + def test_middle_abstract_attributes(self): + # test for [ticket:3219] + class A(Base): + __tablename__ = 'a' + + id = Column(Integer, primary_key=True) + name = Column(String) + + class B(A): + __abstract__ = True + data = Column(String) + + class C(B): + c_value = Column(String) + + eq_( + sa.inspect(C).attrs.keys(), ['id', 'name', 'data', 'c_value'] + ) + + def test_middle_abstract_inherits(self): + # test for [ticket:3240] + + class A(Base): + __tablename__ = 'a' + id = Column(Integer, primary_key=True) + + class AAbs(A): + __abstract__ = True + + class B1(A): + __tablename__ = 'b1' + id = Column(ForeignKey('a.id'), primary_key=True) + + class B2(AAbs): + __tablename__ = 'b2' + id = Column(ForeignKey('a.id'), primary_key=True) + + assert B1.__mapper__.inherits is A.__mapper__ + + assert B2.__mapper__.inherits is A.__mapper__ diff --git a/test/ext/test_associationproxy.py b/test/ext/test_associationproxy.py index 67e474705..9e328a35f 100644 --- a/test/ext/test_associationproxy.py +++ b/test/ext/test_associationproxy.py @@ -912,6 +912,22 @@ class LazyLoadTest(fixtures.TestBase): self.assert_('_children' in p.__dict__) self.assert_(len(p._children) == 3) + def test_slicing_list(self): + Parent, Child = self.Parent, self.Child + + mapper(Parent, self.table, properties={ + '_children': relationship(Child, lazy='select', + collection_class=list)}) + + p = Parent('p') + p.children = ['a', 'b', 'c'] + + p = self.roundtrip(p) + + self.assert_(len(p._children) == 3) + eq_('b', p.children[1]) + eq_(['b', 'c'], p.children[-2:]) + def test_lazy_scalar(self): Parent, Child = self.Parent, self.Child diff --git a/test/ext/test_baked.py b/test/ext/test_baked.py new file mode 100644 index 000000000..61d0fe126 --- /dev/null +++ b/test/ext/test_baked.py @@ -0,0 +1,768 @@ +from sqlalchemy.orm import Session, subqueryload, \ + mapper, relationship, lazyload, clear_mappers +from sqlalchemy.testing import eq_, is_, is_not_, assert_raises +from sqlalchemy import testing +from test.orm import _fixtures +from sqlalchemy.ext.baked import BakedQuery, baked_lazyload, BakedLazyLoader +from sqlalchemy.ext import baked +from sqlalchemy import bindparam, func +from sqlalchemy.orm import exc as orm_exc +import itertools +from sqlalchemy.testing import mock + + +class BakedTest(_fixtures.FixtureTest): + run_setup_mappers = 'once' + run_inserts = 'once' + run_deletes = None + + def setup(self): + self.bakery = baked.bakery() + + +class StateChangeTest(BakedTest): + @classmethod + def setup_mappers(cls): + User = cls.classes.User + + mapper(User, cls.tables.users) + + def _assert_cache_key(self, key, elements): + eq_( + key, + tuple(elem.__code__ for elem in elements) + ) + + def test_initial_key(self): + User = self.classes.User + session = Session() + l1 = lambda: session.query(User) + q1 = self.bakery(l1) + self._assert_cache_key( + q1._cache_key, + [l1] + ) + eq_(q1.steps, [l1]) + + def test_inplace_add(self): + User = self.classes.User + session = Session() + l1 = lambda: session.query(User) + l2 = lambda q: q.filter(User.name == bindparam('name')) + q1 = self.bakery(l1) + self._assert_cache_key( + q1._cache_key, + [l1] + ) + eq_(q1.steps, [l1]) + + q2 = q1.add_criteria(l2) + is_(q2, q1) + + self._assert_cache_key( + q1._cache_key, + [l1, l2] + ) + eq_(q1.steps, [l1, l2]) + + def test_inplace_add_operator(self): + User = self.classes.User + session = Session() + l1 = lambda: session.query(User) + l2 = lambda q: q.filter(User.name == bindparam('name')) + q1 = self.bakery(l1) + self._assert_cache_key( + q1._cache_key, + [l1] + ) + + q1 += l2 + + self._assert_cache_key( + q1._cache_key, + [l1, l2] + ) + + def test_chained_add(self): + User = self.classes.User + session = Session() + l1 = lambda: session.query(User) + l2 = lambda q: q.filter(User.name == bindparam('name')) + q1 = self.bakery(l1) + + q2 = q1.with_criteria(l2) + is_not_(q2, q1) + + self._assert_cache_key( + q1._cache_key, + [l1] + ) + self._assert_cache_key( + q2._cache_key, + [l1, l2] + ) + + def test_chained_add_operator(self): + User = self.classes.User + session = Session() + l1 = lambda: session.query(User) + l2 = lambda q: q.filter(User.name == bindparam('name')) + q1 = self.bakery(l1) + + q2 = q1 + l2 + is_not_(q2, q1) + + self._assert_cache_key( + q1._cache_key, + [l1] + ) + self._assert_cache_key( + q2._cache_key, + [l1, l2] + ) + + +class LikeQueryTest(BakedTest): + @classmethod + def setup_mappers(cls): + User = cls.classes.User + + mapper(User, cls.tables.users) + + def test_first_no_result(self): + User = self.classes.User + + bq = self.bakery(lambda s: s.query(User)) + bq += lambda q: q.filter(User.name == 'asdf') + + eq_( + bq(Session()).first(), + None + ) + + def test_first_multiple_result(self): + User = self.classes.User + + bq = self.bakery(lambda s: s.query(User.id)) + bq += lambda q: q.filter(User.name.like('%ed%')).order_by(User.id) + + eq_( + bq(Session()).first(), + (8, ) + ) + + def test_one_no_result(self): + User = self.classes.User + + bq = self.bakery(lambda s: s.query(User)) + bq += lambda q: q.filter(User.name == 'asdf') + + assert_raises( + orm_exc.NoResultFound, + bq(Session()).one + ) + + def test_one_multiple_result(self): + User = self.classes.User + + bq = self.bakery(lambda s: s.query(User)) + bq += lambda q: q.filter(User.name.like('%ed%')) + + assert_raises( + orm_exc.MultipleResultsFound, + bq(Session()).one + ) + + def test_get(self): + User = self.classes.User + + bq = self.bakery(lambda s: s.query(User)) + + sess = Session() + + def go(): + u1 = bq(sess).get(7) + eq_(u1.name, 'jack') + self.assert_sql_count(testing.db, go, 1) + + u1 = sess.query(User).get(7) # noqa + + def go(): + u2 = bq(sess).get(7) + eq_(u2.name, 'jack') + self.assert_sql_count(testing.db, go, 0) + + def go(): + u2 = bq(sess).get(8) + eq_(u2.name, 'ed') + self.assert_sql_count(testing.db, go, 1) + + def test_get_pk_w_null(self): + """test the re-implementation of logic to do get with IS NULL.""" + + class AddressUser(object): + pass + mapper( + AddressUser, + self.tables.users.outerjoin(self.tables.addresses), + properties={ + "id": self.tables.users.c.id, + "address_id": self.tables.addresses.c.id + } + ) + + bq = self.bakery(lambda s: s.query(AddressUser)) + + sess = Session() + + def go(): + u1 = bq(sess).get((10, None)) + eq_(u1.name, 'chuck') + self.assert_sql_count(testing.db, go, 1) + + u1 = sess.query(AddressUser).get((10, None)) # noqa + + def go(): + u2 = bq(sess).get((10, None)) + eq_(u2.name, 'chuck') + self.assert_sql_count(testing.db, go, 0) + + +class ResultTest(BakedTest): + __backend__ = True + + @classmethod + def setup_mappers(cls): + User = cls.classes.User + Address = cls.classes.Address + + mapper(User, cls.tables.users, properties={ + "addresses": relationship( + Address, order_by=cls.tables.addresses.c.id) + }) + mapper(Address, cls.tables.addresses) + + def test_no_steps(self): + User = self.classes.User + + bq = self.bakery( + lambda s: s.query(User.id, User.name).order_by(User.id)) + + for i in range(3): + session = Session() + eq_( + bq(session).all(), + [(7, 'jack'), (8, 'ed'), (9, 'fred'), (10, 'chuck')] + ) + + def test_different_limits(self): + User = self.classes.User + + bq = self.bakery( + lambda s: s.query(User.id, User.name).order_by(User.id)) + + bq += lambda q: q.limit(bindparam('limit')).offset(bindparam('offset')) + session = Session() + + for i in range(4): + for limit, offset, exp in [ + (2, 1, [(8, 'ed'), (9, 'fred')]), + (3, 0, [(7, 'jack'), (8, 'ed'), (9, 'fred')]), + (1, 2, [(9, 'fred')]) + ]: + eq_( + bq(session).params(limit=limit, offset=offset).all(), + exp + ) + + def test_spoiled_full_w_params(self): + User = self.classes.User + + canary = mock.Mock() + + def fn1(s): + canary.fn1() + return s.query(User.id, User.name).order_by(User.id) + + def fn2(q): + canary.fn2() + return q.filter(User.id == bindparam('id')) + + def fn3(q): + canary.fn3() + return q + + for x in range(3): + bq = self.bakery(fn1) + + bq += fn2 + + sess = Session() + eq_( + bq.spoil(full=True).add_criteria(fn3)(sess).params(id=7).all(), + [(7, 'jack')] + ) + + eq_( + canary.mock_calls, + [mock.call.fn1(), mock.call.fn2(), mock.call.fn3(), + mock.call.fn1(), mock.call.fn2(), mock.call.fn3(), + mock.call.fn1(), mock.call.fn2(), mock.call.fn3()] + ) + + def test_spoiled_half_w_params(self): + User = self.classes.User + + canary = mock.Mock() + + def fn1(s): + canary.fn1() + return s.query(User.id, User.name).order_by(User.id) + + def fn2(q): + canary.fn2() + return q.filter(User.id == bindparam('id')) + + def fn3(q): + canary.fn3() + return q + + bq = self.bakery(fn1) + + bq += fn2 + + for x in range(3): + bq = self.bakery(fn1) + + bq += fn2 + + sess = Session() + eq_( + bq.spoil().add_criteria(fn3)(sess).params(id=7).all(), + [(7, 'jack')] + ) + + eq_( + canary.mock_calls, + [mock.call.fn1(), mock.call.fn2(), + mock.call.fn3(), mock.call.fn3(), mock.call.fn3()] + ) + + def test_w_new_entities(self): + """Test that the query can have its entities modified in + an arbitrary callable, and that this new entity list is preserved + when the query is invoked. + + """ + User = self.classes.User + + bq = self.bakery( + lambda s: s.query(User.id, User.name)) + + bq += lambda q: q.from_self().with_entities( + func.count(User.id)) + + for i in range(3): + session = Session() + eq_( + bq(session).all(), + [(4, )] + ) + + def test_conditional_step(self): + """Test a large series of conditionals and assert that + results remain correct between all of them within a series + of loops. + + """ + User = self.classes.User + + base_bq = self.bakery( + lambda s: s.query(User.id, User.name)) + + base_bq += lambda q: q.order_by(User.id) + + for i in range(4): + for cond1, cond2, cond3, cond4 in itertools.product( + *[(False, True) for j in range(4)]): + bq = base_bq._clone() + if cond1: + bq += lambda q: q.filter(User.name != 'jack') + if cond2: + bq += lambda q: q.join(User.addresses) + else: + bq += lambda q: q.outerjoin(User.addresses) + elif cond3: + bq += lambda q: q.filter(User.name.like('%ed%')) + else: + bq += lambda q: q.filter(User.name == 'jack') + + if cond4: + bq += lambda q: q.from_self().with_entities( + func.count(User.id)) + sess = Session() + result = bq(sess).all() + if cond4: + if cond1: + if cond2: + eq_(result, [(4,)]) + else: + eq_(result, [(5,)]) + elif cond3: + eq_(result, [(2,)]) + else: + eq_(result, [(1,)]) + else: + if cond1: + if cond2: + eq_( + result, + [(8, 'ed'), (8, 'ed'), (8, 'ed'), + (9, 'fred')] + ) + else: + eq_( + result, + [(8, 'ed'), (8, 'ed'), (8, 'ed'), + (9, 'fred'), (10, 'chuck')] + ) + elif cond3: + eq_(result, [(8, 'ed'), (9, 'fred')]) + else: + eq_(result, [(7, 'jack')]) + + sess.close() + + def test_conditional_step_oneline(self): + User = self.classes.User + + base_bq = self.bakery( + lambda s: s.query(User.id, User.name)) + + base_bq += lambda q: q.order_by(User.id) + + for i in range(4): + for cond1 in (False, True): + bq = base_bq._clone() + + # we were using (filename, firstlineno) as cache key, + # which fails for this kind of thing! + bq += (lambda q: q.filter(User.name != 'jack')) if cond1 else (lambda q: q.filter(User.name == 'jack')) # noqa + sess = Session() + result = bq(sess).all() + + if cond1: + eq_(result, [(8, u'ed'), (9, u'fred'), (10, u'chuck')]) + else: + eq_(result, [(7, 'jack')]) + + sess.close() + + def test_subquery_eagerloading(self): + User = self.classes.User + Address = self.classes.Address + + base_bq = self.bakery( + lambda s: s.query(User)) + + base_bq += lambda q: q.options(subqueryload(User.addresses)) + base_bq += lambda q: q.order_by(User.id) + + assert_result = [ + User(id=7, addresses=[ + Address(id=1, email_address='jack@bean.com')]), + User(id=8, addresses=[ + Address(id=2, email_address='ed@wood.com'), + Address(id=3, email_address='ed@bettyboop.com'), + Address(id=4, email_address='ed@lala.com'), + ]), + User(id=9, addresses=[ + Address(id=5) + ]), + User(id=10, addresses=[]) + ] + + for i in range(4): + for cond1, cond2 in itertools.product( + *[(False, True) for j in range(2)]): + bq = base_bq._clone() + + sess = Session() + + if cond1: + bq += lambda q: q.filter(User.name == 'jack') + else: + bq += lambda q: q.filter(User.name.like('%ed%')) + + if cond2: + ct = func.count(Address.id).label('count') + subq = sess.query( + ct, + Address.user_id).group_by(Address.user_id).\ + having(ct > 2).subquery() + + bq += lambda q: q.join(subq) + + if cond2: + if cond1: + def go(): + result = bq(sess).all() + eq_([], result) + self.assert_sql_count(testing.db, go, 1) + else: + def go(): + result = bq(sess).all() + eq_(assert_result[1:2], result) + self.assert_sql_count(testing.db, go, 2) + else: + if cond1: + def go(): + result = bq(sess).all() + eq_(assert_result[0:1], result) + self.assert_sql_count(testing.db, go, 2) + else: + def go(): + result = bq(sess).all() + eq_(assert_result[1:3], result) + self.assert_sql_count(testing.db, go, 2) + + sess.close() + + +class LazyLoaderTest(BakedTest): + run_setup_mappers = 'each' + + def _o2m_fixture(self, lazy="select"): + User = self.classes.User + Address = self.classes.Address + + mapper(User, self.tables.users, properties={ + 'addresses': relationship( + Address, order_by=self.tables.addresses.c.id, + lazy=lazy) + }) + mapper(Address, self.tables.addresses) + return User, Address + + def _m2o_fixture(self): + User = self.classes.User + Address = self.classes.Address + + mapper(User, self.tables.users) + mapper(Address, self.tables.addresses, properties={ + 'user': relationship(User) + }) + return User, Address + + def test_strategy_lookup(self): + """test that the lazy loader strategies aren't getting mixed up + with BakedLazyLoader as a subclass. + + """ + User, Address = self._o2m_fixture() + + ll = User.addresses.property._get_strategy((('lazy', 'select'),)) + assert not isinstance(ll, BakedLazyLoader) + eq_(ll._strategy_keys, [(('lazy', 'select'),), (('lazy', True),)]) + + ll = User.addresses.property._get_strategy((('lazy', True),)) + assert not isinstance(ll, BakedLazyLoader) + eq_(ll._strategy_keys, [(('lazy', 'select'),), (('lazy', True),)]) + + bl = User.addresses.property._get_strategy((('lazy', 'baked_select'),)) + assert isinstance(bl, BakedLazyLoader) + eq_(bl._strategy_keys, [(('lazy', 'baked_select'),)]) + + def test_invocation_per_state(self): + """test that BakedLazyLoader is getting invoked with the + baked_lazyload() loader. + + """ + User, Address = self._o2m_fixture() + + sess = Session() + q = sess.query(User) + + with mock.patch.object(BakedLazyLoader, "_emit_lazyload") as el: + u1 = q.first() + u1.addresses + # not invoked + eq_(el.mock_calls, []) + + sess = Session() + q = sess.query(User).options(baked_lazyload(User.addresses)) + with mock.patch.object(BakedLazyLoader, "_emit_lazyload") as el: + u1 = q.first() + u1.addresses + # invoked + is_( + el.mock_calls[0][1][1], + u1._sa_instance_state + ) + + def test_invocation_per_mapper(self): + """test that BakedLazyLoader is getting invoked with the + "baked_select" lazy setting. + + """ + User, Address = self._o2m_fixture(lazy="baked_select") + + sess = Session() + q = sess.query(User).options(lazyload(User.addresses)) + + with mock.patch.object(BakedLazyLoader, "_emit_lazyload") as el: + u1 = q.first() + u1.addresses + # not invoked + eq_(el.mock_calls, []) + + sess = Session() + q = sess.query(User) + with mock.patch.object(BakedLazyLoader, "_emit_lazyload") as el: + u1 = q.first() + u1.addresses + # invoked + is_( + el.mock_calls[0][1][1], + u1._sa_instance_state + ) + + def test_invocation_systemwide_loaders(self): + baked.bake_lazy_loaders() + try: + User, Address = self._o2m_fixture() + + sess = Session() + q = sess.query(User).options(lazyload(User.addresses)) + with mock.patch.object(BakedLazyLoader, "_emit_lazyload") as el: + u1 = q.first() + u1.addresses + # invoked + is_( + el.mock_calls[0][1][1], + u1._sa_instance_state + ) + finally: + baked.unbake_lazy_loaders() + + clear_mappers() + User, Address = self._o2m_fixture() + sess = Session() + q = sess.query(User).options(lazyload(User.addresses)) + + with mock.patch.object(BakedLazyLoader, "_emit_lazyload") as el: + u1 = q.first() + u1.addresses + # not invoked + eq_(el.mock_calls, []) + + def test_baked_lazy_loading_option_o2m(self): + User, Address = self._o2m_fixture() + self._test_baked_lazy_loading(set_option=True) + + def test_baked_lazy_loading_mapped_o2m(self): + User, Address = self._o2m_fixture(lazy="baked_select") + self._test_baked_lazy_loading(set_option=False) + + def _test_baked_lazy_loading(self, set_option): + User, Address = self.classes.User, self.classes.Address + + base_bq = self.bakery( + lambda s: s.query(User)) + + if set_option: + base_bq += lambda q: q.options(baked_lazyload(User.addresses)) + + base_bq += lambda q: q.order_by(User.id) + + assert_result = self.static.user_address_result + + for i in range(4): + for cond1, cond2 in itertools.product( + *[(False, True) for j in range(2)]): + bq = base_bq._clone() + + sess = Session() + + if cond1: + bq += lambda q: q.filter(User.name == 'jack') + else: + bq += lambda q: q.filter(User.name.like('%ed%')) + + if cond2: + ct = func.count(Address.id).label('count') + subq = sess.query( + ct, + Address.user_id).group_by(Address.user_id).\ + having(ct > 2).subquery() + + bq += lambda q: q.join(subq) + + if cond2: + if cond1: + def go(): + result = bq(sess).all() + eq_([], result) + self.assert_sql_count(testing.db, go, 1) + else: + def go(): + result = bq(sess).all() + eq_(assert_result[1:2], result) + self.assert_sql_count(testing.db, go, 2) + else: + if cond1: + def go(): + result = bq(sess).all() + eq_(assert_result[0:1], result) + self.assert_sql_count(testing.db, go, 2) + else: + def go(): + result = bq(sess).all() + eq_(assert_result[1:3], result) + self.assert_sql_count(testing.db, go, 3) + + sess.close() + + def test_baked_lazy_loading_m2o(self): + User, Address = self._m2o_fixture() + + base_bq = self.bakery( + lambda s: s.query(Address)) + + base_bq += lambda q: q.options(baked_lazyload(Address.user)) + base_bq += lambda q: q.order_by(Address.id) + + assert_result = self.static.address_user_result + + for i in range(4): + for cond1 in (False, True): + bq = base_bq._clone() + + sess = Session() + + if cond1: + bq += lambda q: q.filter( + Address.email_address == 'jack@bean.com') + else: + bq += lambda q: q.filter( + Address.email_address.like('ed@%')) + + if cond1: + def go(): + result = bq(sess).all() + eq_(assert_result[0:1], result) + self.assert_sql_count(testing.db, go, 2) + else: + def go(): + result = bq(sess).all() + eq_(assert_result[1:4], result) + self.assert_sql_count(testing.db, go, 2) + + sess.close() + + # additional tests: + # 1. m2m w lazyload + # 2. o2m lazyload where m2o backrefs have an eager load, test + # that eager load is canceled out + # 3. uselist = False, uselist=False assertion + diff --git a/test/ext/test_extendedattr.py b/test/ext/test_extendedattr.py index 352b6b241..c7627c8b2 100644 --- a/test/ext/test_extendedattr.py +++ b/test/ext/test_extendedattr.py @@ -485,5 +485,5 @@ class ExtendedEventsTest(fixtures.ORMTest): register_class(A) manager = instrumentation.manager_of_class(A) - assert issubclass(manager.dispatch._parent_cls.__dict__['dispatch'].events, MyEvents) + assert issubclass(manager.dispatch._events, MyEvents) diff --git a/test/ext/test_horizontal_shard.py b/test/ext/test_horizontal_shard.py index 99879a74d..0af33ecde 100644 --- a/test/ext/test_horizontal_shard.py +++ b/test/ext/test_horizontal_shard.py @@ -235,8 +235,6 @@ class AttachedFileShardTest(ShardTest, fixtures.TestBase): def _init_dbs(self): db1 = testing_engine('sqlite://', options={"execution_options": {"shard_id": "shard1"}}) - assert db1._has_events - db2 = db1.execution_options(shard_id="shard2") db3 = db1.execution_options(shard_id="shard3") db4 = db1.execution_options(shard_id="shard4") |