[Sqlalchemy-commits] [1215] sqlalchemy/trunk/test: rework to expire() to make it smarter.
Brought to you by:
zzzeek
From: <co...@sq...> - 2006-03-26 21:44:34
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1215] sqlalchemy/trunk/test: rework to expire() to make it smarter.</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1215</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-03-26 15:44:22 -0600 (Sun, 26 Mar 2006)</dd> </dl> <h3>Log Message</h3> <pre>rework to expire() to make it smarter. when you expire(), history is immediately removed as well as explicit from dirty/deleted lists. this also changes uow.rollback_object() to remove from those lists, which is strange that it didnt do that before. anyway the mapper, when selecting and creating instances, asks the uow if this already identity-mapped instance is expired, and if so refreshes it on the fly, saving the need for the re-_get() operation, if some other query happens to touch upon the expired object. unit test added to confirm this.</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemyattributespy">sqlalchemy/trunk/lib/sqlalchemy/attributes.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemymappingmapperpy">sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemymappingobjectstorepy">sqlalchemy/trunk/lib/sqlalchemy/mapping/objectstore.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemymappingunitofworkpy">sqlalchemy/trunk/lib/sqlalchemy/mapping/unitofwork.py</a></li> <li><a href="#sqlalchemytrunktestmapperpy">sqlalchemy/trunk/test/mapper.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunklibsqlalchemyattributespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/attributes.py (1214 => 1215)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/attributes.py 2006-03-26 19:58:08 UTC (rev 1214) +++ sqlalchemy/trunk/lib/sqlalchemy/attributes.py 2006-03-26 21:44:22 UTC (rev 1215) </span><span class="lines">@@ -398,7 +398,13 @@ </span><span class="cx"> except KeyError: </span><span class="cx"> pass </span><span class="cx"> obj.__dict__['_managed_trigger'] = callable </span><ins>+ + def untrigger_history(self, obj): + del obj.__dict__['_managed_trigger'] </ins><span class="cx"> </span><ins>+ def has_trigger(self, obj): + return obj.__dict__.has_key('_managed_trigger') + </ins><span class="cx"> def reset_history(self, obj, key): </span><span class="cx"> """removes the history object for the given attribute on the given object. </span><span class="cx"> When the attribute is next accessed, a new container will be created via the </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemymappingmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py (1214 => 1215)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py 2006-03-26 19:58:08 UTC (rev 1214) +++ sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py 2006-03-26 21:44:22 UTC (rev 1215) </span><span class="lines">@@ -816,16 +816,16 @@ </span><span class="cx"> # including modifying any of its related items lists, as its already </span><span class="cx"> # been exposed to being modified by the application. </span><span class="cx"> identitykey = self._identity_key(row) </span><del>- if objectstore.get_session().has_key(identitykey): - instance = objectstore.get_session()._get(identitykey) </del><ins>+ sess = objectstore.get_session() + if sess.has_key(identitykey): + instance = sess._get(identitykey) </ins><span class="cx"> </span><span class="cx"> isnew = False </span><del>- if populate_existing: </del><ins>+ if populate_existing or sess.is_expired(instance, unexpire=True): </ins><span class="cx"> if not imap.has_key(identitykey): </span><span class="cx"> imap[identitykey] = instance </span><span class="cx"> for prop in self.props.values(): </span><span class="cx"> prop.execute(instance, row, identitykey, imap, True) </span><del>- </del><span class="cx"> if self.extension.append_result(self, row, imap, result, instance, isnew, populate_existing=populate_existing): </span><span class="cx"> if result is not None: </span><span class="cx"> result.append_nohistory(instance) </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemymappingobjectstorepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/mapping/objectstore.py (1214 => 1215)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/mapping/objectstore.py 2006-03-26 19:58:08 UTC (rev 1214) +++ sqlalchemy/trunk/lib/sqlalchemy/mapping/objectstore.py 2006-03-26 21:44:22 UTC (rev 1215) </span><span class="lines">@@ -166,7 +166,7 @@ </span><span class="cx"> """invalidates the data in the given objects and sets them to refresh themselves </span><span class="cx"> the next time they are requested.""" </span><span class="cx"> for o in obj: </span><del>- global_attributes.trigger_history(o, lambda: refresh(o)) </del><ins>+ self.uow.expire(o) </ins><span class="cx"> </span><span class="cx"> def expunge(self, *obj): </span><span class="cx"> for o in obj: </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemymappingunitofworkpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/mapping/unitofwork.py (1214 => 1215)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/mapping/unitofwork.py 2006-03-26 19:58:08 UTC (rev 1214) +++ sqlalchemy/trunk/lib/sqlalchemy/mapping/unitofwork.py 2006-03-26 21:44:22 UTC (rev 1215) </span><span class="lines">@@ -100,6 +100,18 @@ </span><span class="cx"> self.rollback_object(obj) </span><span class="cx"> object_mapper(obj)._get(obj._instance_key, reload=True) </span><span class="cx"> </span><ins>+ def expire(self, obj): + self.rollback_object(obj) + def exp(): + object_mapper(obj)._get(obj._instance_key, reload=True) + global_attributes.trigger_history(obj, exp) + + def is_expired(self, obj, unexpire=False): + ret = global_attributes.has_trigger(obj) + if ret and unexpire: + global_attributes.untrigger_history(obj) + return ret + </ins><span class="cx"> def has_key(self, key): </span><span class="cx"> """returns True if the given key is present in this UnitOfWork's identity map.""" </span><span class="cx"> return self.identity_map.has_key(key) </span><span class="lines">@@ -247,6 +259,14 @@ </span><span class="cx"> def rollback_object(self, obj): </span><span class="cx"> """'rolls back' the attributes that have been changed on an object instance.""" </span><span class="cx"> self.attributes.rollback(obj) </span><ins>+ try: + del self.dirty[obj] + except KeyError: + pass + try: + del self.deleted[obj] + except KeyError: + pass </ins><span class="cx"> </span><span class="cx"> class UOWTransaction(object): </span><span class="cx"> """handles the details of organizing and executing transaction tasks </span></span></pre></div> <a id="sqlalchemytrunktestmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/test/mapper.py (1214 => 1215)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/mapper.py 2006-03-26 19:58:08 UTC (rev 1214) +++ sqlalchemy/trunk/test/mapper.py 2006-03-26 21:44:22 UTC (rev 1215) </span><span class="lines">@@ -111,14 +111,36 @@ </span><span class="cx"> self.assert_(a in u.addresses) </span><span class="cx"> objectstore.expire(u) </span><span class="cx"> </span><del>- # expired, but not refreshed yet. still dirty - self.assert_(u in objectstore.get_session().uow.dirty) </del><span class="cx"> # get the attribute, it refreshes </span><span class="cx"> self.assert_(u.user_name == 'jack') </span><span class="cx"> self.assert_(a not in u.addresses) </span><del>- # not dirty anymore - self.assert_(u not in objectstore.get_session().uow.dirty) - </del><ins>+ + def testexpire(self): + m = mapper(User, users, properties={'addresses':relation(mapper(Address, addresses))}) + u = m.get(7) + u.user_name = 'foo' + objectstore.expire(u) + # test plain expire + self.assert_(u.user_name =='jack') + + # we're changing the database here, so if this test fails in the middle, + # it'll screw up the other tests which are hardcoded to 7/'jack' + u.user_name = 'foo' + objectstore.commit() + # change the value in the DB + users.update(users.c.user_id==7, values=dict(user_name='jack')).execute() + objectstore.expire(u) + # object isnt refreshed yet, using dict to bypass trigger + self.assert_(u.__dict__['user_name'] != 'jack') + # do a select + m.select() + # test that it refreshed + self.assert_(u.__dict__['user_name'] == 'jack') + + # object should be back to normal now, + # this should *not* produce a SELECT statement (not tested here though....) + self.assert_(u.user_name =='jack') + </ins><span class="cx"> def testrefresh2(self): </span><span class="cx"> assign_mapper(Address, addresses) </span><span class="cx"> </span></span></pre> </div> </div> </body> </html> |