📄 expire.py
字号:
"""test attribute/instance expiration, deferral of attributes, etc."""import testenv; testenv.configure_for_tests()from sqlalchemy import *from sqlalchemy import exceptionsfrom sqlalchemy.orm import *from testlib import *from testlib.fixtures import *import gcclass ExpireTest(FixtureTest): keep_mappers = False refresh_data = True def test_expire(self): mapper(User, users, properties={ 'addresses':relation(Address, backref='user'), }) mapper(Address, addresses) sess = create_session() u = sess.query(User).get(7) assert len(u.addresses) == 1 u.name = 'foo' del u.addresses[0] sess.expire(u) assert 'name' not in u.__dict__ def go(): assert u.name == 'jack' self.assert_sql_count(testing.db, go, 1) assert 'name' in u.__dict__ u.name = 'foo' sess.flush() # change the value in the DB users.update(users.c.id==7, values=dict(name='jack')).execute() sess.expire(u) # object isnt refreshed yet, using dict to bypass trigger assert u.__dict__.get('name') != 'jack' sess.query(User).all() # test that it refreshed assert u.__dict__['name'] == 'jack' def go(): assert u.name == 'jack' self.assert_sql_count(testing.db, go, 0) def test_expire_doesntload_on_set(self): mapper(User, users) sess = create_session() u = sess.query(User).get(7) sess.expire(u, attribute_names=['name']) def go(): u.name = 'somenewname' self.assert_sql_count(testing.db, go, 0) sess.flush() sess.clear() assert sess.query(User).get(7).name == 'somenewname' def test_no_session(self): mapper(User, users) sess = create_session() u = sess.query(User).get(7) sess.expire(u, attribute_names=['name']) sess.expunge(u) try: u.name except exceptions.InvalidRequestError, e: assert str(e) == "Instance <class 'testlib.fixtures.User'> is not bound to a Session, and no contextual session is established; attribute refresh operation cannot proceed" def test_no_instance_key(self): # this tests an artificial condition such that # an instance is pending, but has expired attributes. this # is actually part of a larger behavior when postfetch needs to # occur during a flush() on an instance that was just inserted mapper(User, users) sess = create_session() u = sess.query(User).get(7) sess.expire(u, attribute_names=['name']) sess.expunge(u) del u._instance_key assert 'name' not in u.__dict__ sess.save(u) assert u.name == 'jack' def test_expire_preserves_changes(self): """test that the expire load operation doesn't revert post-expire changes""" mapper(Order, orders) sess = create_session() o = sess.query(Order).get(3) sess.expire(o) o.description = "order 3 modified" def go(): assert o.isopen == 1 self.assert_sql_count(testing.db, go, 1) assert o.description == 'order 3 modified' del o.description assert "description" not in o.__dict__ sess.expire(o, ['isopen']) sess.query(Order).all() assert o.isopen == 1 assert "description" not in o.__dict__ assert o.description is None o.isopen=15 sess.expire(o, ['isopen', 'description']) o.description = 'some new description' sess.query(Order).all() assert o.isopen == 1 assert o.description == 'some new description' sess.expire(o, ['isopen', 'description']) sess.query(Order).all() del o.isopen def go(): assert o.isopen is None self.assert_sql_count(testing.db, go, 0) o.isopen=14 sess.expire(o) o.description = 'another new description' sess.query(Order).all() assert o.isopen == 1 assert o.description == 'another new description' def test_expire_committed(self): """test that the committed state of the attribute receives the most recent DB data""" mapper(Order, orders) sess = create_session() o = sess.query(Order).get(3) sess.expire(o) orders.update(id=3).execute(description='order 3 modified') assert o.isopen == 1 assert o._state.dict['description'] == 'order 3 modified' def go(): sess.flush() self.assert_sql_count(testing.db, go, 0) def test_expire_cascade(self): mapper(User, users, properties={ 'addresses':relation(Address, cascade="all, refresh-expire") }) mapper(Address, addresses) s = create_session() u = s.get(User, 8) assert u.addresses[0].email_address == 'ed@wood.com' u.addresses[0].email_address = 'someotheraddress' s.expire(u) u.name print u._state.dict assert u.addresses[0].email_address == 'ed@wood.com' def test_expired_lazy(self): mapper(User, users, properties={ 'addresses':relation(Address, backref='user'), }) mapper(Address, addresses) sess = create_session() u = sess.query(User).get(7) sess.expire(u) assert 'name' not in u.__dict__ assert 'addresses' not in u.__dict__ def go(): assert u.addresses[0].email_address == 'jack@bean.com' assert u.name == 'jack' # two loads self.assert_sql_count(testing.db, go, 2) assert 'name' in u.__dict__ assert 'addresses' in u.__dict__ def test_expired_eager(self): mapper(User, users, properties={ 'addresses':relation(Address, backref='user', lazy=False), }) mapper(Address, addresses) sess = create_session() u = sess.query(User).get(7) sess.expire(u) assert 'name' not in u.__dict__ assert 'addresses' not in u.__dict__ def go(): assert u.addresses[0].email_address == 'jack@bean.com' assert u.name == 'jack' # two loads, since relation() + scalar are # separate right now on per-attribute load self.assert_sql_count(testing.db, go, 2) assert 'name' in u.__dict__ assert 'addresses' in u.__dict__ sess.expire(u, ['name', 'addresses']) assert 'name' not in u.__dict__ assert 'addresses' not in u.__dict__ def go(): sess.query(User).filter_by(id=7).one() assert u.addresses[0].email_address == 'jack@bean.com' assert u.name == 'jack' # one load, since relation() + scalar are # together when eager load used with Query self.assert_sql_count(testing.db, go, 1) def test_relation_changes_preserved(self): mapper(User, users, properties={ 'addresses':relation(Address, backref='user', lazy=False), }) mapper(Address, addresses) sess = create_session() u = sess.query(User).get(8) sess.expire(u, ['name', 'addresses']) u.addresses assert 'name' not in u.__dict__ del u.addresses[1] u.name assert 'name' in u.__dict__ assert len(u.addresses) == 2 def test_eagerload_props_dontload(self): # relations currently have to load separately from scalar instances. the use case is: # expire "addresses". then access it. lazy load fires off to load "addresses", but needs # foreign key or primary key attributes in order to lazy load; hits those attributes, # such as below it hits "u.id". "u.id" triggers full unexpire operation, eagerloads # addresses since lazy=False. this is all wihtin lazy load which fires unconditionally; # so an unnecessary eagerload (or lazyload) was issued. would prefer not to complicate # lazyloading to "figure out" that the operation should be aborted right now. mapper(User, users, properties={ 'addresses':relation(Address, backref='user', lazy=False), }) mapper(Address, addresses) sess = create_session() u = sess.query(User).get(8) sess.expire(u) u.id assert 'addresses' not in u.__dict__ u.addresses assert 'addresses' in u.__dict__ def test_expire_synonym(self): mapper(User, users, properties={ 'uname':synonym('name') }) sess = create_session() u = sess.query(User).get(7) assert 'name' in u.__dict__ assert u.uname == u.name sess.expire(u) assert 'name' not in u.__dict__ users.update(users.c.id==7).execute(name='jack2') assert u.name == 'jack2' assert u.uname == 'jack2' assert 'name' in u.__dict__ # this wont work unless we add API hooks through the attr. system # to provide "expire" behavior on a synonym #sess.expire(u, ['uname']) #users.update(users.c.id==7).execute(name='jack3') #assert u.uname == 'jack3' def test_partial_expire(self): mapper(Order, orders) sess = create_session() o = sess.query(Order).get(3) sess.expire(o, attribute_names=['description']) assert 'id' in o.__dict__ assert 'description' not in o.__dict__ assert o._state.dict['isopen'] == 1 orders.update(orders.c.id==3).execute(description='order 3 modified') def go(): assert o.description == 'order 3 modified' self.assert_sql_count(testing.db, go, 1) assert o._state.dict['description'] == 'order 3 modified' o.isopen = 5 sess.expire(o, attribute_names=['description']) assert 'id' in o.__dict__ assert 'description' not in o.__dict__ assert o.__dict__['isopen'] == 5 assert o._state.committed_state['isopen'] == 1 def go(): assert o.description == 'order 3 modified' self.assert_sql_count(testing.db, go, 1) assert o.__dict__['isopen'] == 5 assert o._state.dict['description'] == 'order 3 modified' assert o._state.committed_state['isopen'] == 1 sess.flush() sess.expire(o, attribute_names=['id', 'isopen', 'description']) assert 'id' not in o.__dict__ assert 'isopen' not in o.__dict__ assert 'description' not in o.__dict__ def go(): assert o.description == 'order 3 modified' assert o.id == 3 assert o.isopen == 5 self.assert_sql_count(testing.db, go, 1) def test_partial_expire_lazy(self): mapper(User, users, properties={ 'addresses':relation(Address, backref='user'), }) mapper(Address, addresses) sess = create_session() u = sess.query(User).get(8) sess.expire(u, ['name', 'addresses']) assert 'name' not in u.__dict__ assert 'addresses' not in u.__dict__ # hit the lazy loader. just does the lazy load, # doesnt do the overall refresh def go(): assert u.addresses[0].email_address=='ed@wood.com' self.assert_sql_count(testing.db, go, 1) assert 'name' not in u.__dict__ # check that mods to expired lazy-load attributes # only do the lazy load sess.expire(u, ['name', 'addresses']) def go(): u.addresses = [Address(id=10, email_address='foo@bar.com')] self.assert_sql_count(testing.db, go, 1) sess.flush() # flush has occurred, and addresses was modified, # so the addresses collection got committed and is # longer expired def go(): assert u.addresses[0].email_address=='foo@bar.com' assert len(u.addresses) == 1 self.assert_sql_count(testing.db, go, 0) # but the name attribute was never loaded and so # still loads def go(): assert u.name == 'ed' self.assert_sql_count(testing.db, go, 1) def test_partial_expire_eager(self): mapper(User, users, properties={ 'addresses':relation(Address, backref='user', lazy=False), })
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -