[Sqlalchemy-commits] [5068] sqlalchemy/trunk: - add an example illustrating attribute event recepti
Brought to you by:
zzzeek
From: <co...@sq...> - 2008-08-29 16:15:46
|
<!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><meta http-equiv="content-type" content="text/html; charset=utf-8" /><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, #header, #footer { 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; } #header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; } #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>[5068] sqlalchemy/trunk: - add an example illustrating attribute event reception.</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>5068</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2008-08-29 12:15:41 -0400 (Fri, 29 Aug 2008)</dd> </dl> <h3>Log Message</h3> <pre>- add an example illustrating attribute event reception.</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemyormattributespy">sqlalchemy/trunk/lib/sqlalchemy/orm/attributes.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemyorminterfacespy">sqlalchemy/trunk/lib/sqlalchemy/orm/interfaces.py</a></li> </ul> <h3>Added Paths</h3> <ul> <li><a href="#sqlalchemytrunkexamplescustom_attributeslisten_for_eventspy">sqlalchemy/trunk/examples/custom_attributes/listen_for_events.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunkexamplescustom_attributeslisten_for_eventspy"></a> <div class="addfile"><h4>Added: sqlalchemy/trunk/examples/custom_attributes/listen_for_events.py (0 => 5068)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/examples/custom_attributes/listen_for_events.py (rev 0) +++ sqlalchemy/trunk/examples/custom_attributes/listen_for_events.py 2008-08-29 16:15:41 UTC (rev 5068) </span><span class="lines">@@ -0,0 +1,83 @@ </span><ins>+""" +Illustrates how to use AttributeExtension to listen for change events. + +""" + +from sqlalchemy.orm.interfaces import AttributeExtension, InstrumentationManager + +class InstallListeners(InstrumentationManager): + def instrument_attribute(self, class_, key, inst): + """Add an event listener to all InstrumentedAttributes.""" + + inst.impl.extensions.append(AttributeListener(key)) + return super(InstallListeners, self).instrument_attribute(class_, key, inst) + +class AttributeListener(AttributeExtension): + """Generic event listener. + + Propigates attribute change events to a + "receive_change_event()" method on the target + instance. + + """ + def __init__(self, key): + self.key = key + + def append(self, state, value, initiator): + self._report(state, value, None, "appended") + + def remove(self, state, value, initiator): + self._report(state, value, None, "removed") + + def set(self, state, value, oldvalue, initiator): + self._report(state, value, oldvalue, "set") + + def _report(self, state, value, oldvalue, verb): + state.obj().receive_change_event(verb, self.key, value, oldvalue) + +if __name__ == '__main__': + + from sqlalchemy import * + from sqlalchemy.orm import * + from sqlalchemy.ext.declarative import declarative_base + + class Base(object): + __sa_instrumentation_manager__ = InstallListeners + + def receive_change_event(self, verb, key, value, oldvalue): + s = "Value '%s' %s on attribute '%s', " % (value, verb, key) + if oldvalue: + s += "which replaced the value '%s', " % oldvalue + s += "on object %s" % self + print s + + Base = declarative_base(cls=Base) + + class MyMappedClass(Base): + __tablename__ = "mytable" + + id = Column(Integer, primary_key=True) + data = Column(String(50)) + related_id = Column(Integer, ForeignKey("related.id")) + related = relation("Related", backref="mapped") + + def __str__(self): + return "MyMappedClass(data=%r)" % self.data + + class Related(Base): + __tablename__ = "related" + + id = Column(Integer, primary_key=True) + data = Column(String(50)) + + def __str__(self): + return "Related(data=%r)" % self.data + + # classes are instrumented. Demonstrate the events ! + + m1 = MyMappedClass(data='m1', related=Related(data='r1')) + m1.data = 'm1mod' + m1.related.mapped.append(MyMappedClass(data='m2')) + del m1.data + + </ins><span class="cx">\ No newline at end of file </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemyormattributespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/orm/attributes.py (5067 => 5068)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/orm/attributes.py 2008-08-29 15:41:43 UTC (rev 5067) +++ sqlalchemy/trunk/lib/sqlalchemy/orm/attributes.py 2008-08-29 16:15:41 UTC (rev 5068) </span><span class="lines">@@ -1545,7 +1545,10 @@ </span><span class="cx"> def state_of(self, instance): </span><span class="cx"> if instance is None: </span><span class="cx"> raise AttributeError("None has no persistent state.") </span><del>- return self.state_finders[instance.__class__](instance) </del><ins>+ try: + return self.state_finders[instance.__class__](instance) + except KeyError: + raise AttributeError("%r is not instrumented" % instance.__class__) </ins><span class="cx"> </span><span class="cx"> def state_or_default(self, instance, default=None): </span><span class="cx"> if instance is None: </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemyorminterfacespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/orm/interfaces.py (5067 => 5068)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/orm/interfaces.py 2008-08-29 15:41:43 UTC (rev 5067) +++ sqlalchemy/trunk/lib/sqlalchemy/orm/interfaces.py 2008-08-29 16:15:41 UTC (rev 5068) </span><span class="lines">@@ -702,17 +702,20 @@ </span><span class="cx"> return l </span><span class="cx"> </span><span class="cx"> class AttributeExtension(object): </span><del>- """An abstract class which specifies `append`, `delete`, and `set` - event handlers to be attached to an object property. </del><ins>+ """An event handler for individual attribute change events. + + AttributeExtension is assembled within the descriptors associated + with a mapped class. + </ins><span class="cx"> """ </span><span class="cx"> </span><del>- def append(self, obj, child, initiator): </del><ins>+ def append(self, state, value, initiator): </ins><span class="cx"> pass </span><span class="cx"> </span><del>- def remove(self, obj, child, initiator): </del><ins>+ def remove(self, state, value, initiator): </ins><span class="cx"> pass </span><span class="cx"> </span><del>- def set(self, obj, child, oldchild, initiator): </del><ins>+ def set(self, state, value, oldvalue, initiator): </ins><span class="cx"> pass </span><span class="cx"> </span><span class="cx"> </span></span></pre> </div> </div> </body> </html> |