[Sqlalchemy-commits] [1359] sqlalchemy/branches/schema/lib/sqlalchemy: vertical example, session stu
Brought to you by:
zzzeek
From: <co...@sq...> - 2006-04-29 18:41:27
|
<!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>[1359] sqlalchemy/branches/schema/lib/sqlalchemy: vertical example, session stuff</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1359</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-29 13:41:13 -0500 (Sat, 29 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>vertical example, session stuff</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemaexamplespolymorphpolymorphpy">sqlalchemy/branches/schema/examples/polymorph/polymorph.py</a></li> <li><a href="#sqlalchemybranchesschemaexamplesverticalverticalpy">sqlalchemy/branches/schema/examples/vertical/vertical.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemy__init__py">sqlalchemy/branches/schema/lib/sqlalchemy/__init__.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyattributespy">sqlalchemy/branches/schema/lib/sqlalchemy/attributes.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemymodsthreadlocalpy">sqlalchemy/branches/schema/lib/sqlalchemy/mods/threadlocal.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormmapperpy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormpropertiespy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/properties.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormquerypy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/query.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormsessionpy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/session.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormunitofworkpy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/unitofwork.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormutilpy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/util.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemaexamplespolymorphpolymorphpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/examples/polymorph/polymorph.py (1358 => 1359)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/examples/polymorph/polymorph.py 2006-04-29 17:16:16 UTC (rev 1358) +++ sqlalchemy/branches/schema/examples/polymorph/polymorph.py 2006-04-29 18:41:13 UTC (rev 1359) </span><span class="lines">@@ -130,7 +130,6 @@ </span><span class="cx"> session.delete(c) </span><span class="cx"> session.flush() </span><span class="cx"> </span><del>- </del><span class="cx"> managers.drop() </span><span class="cx"> engineers.drop() </span><span class="cx"> people.drop() </span></span></pre></div> <a id="sqlalchemybranchesschemaexamplesverticalverticalpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/examples/vertical/vertical.py (1358 => 1359)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/examples/vertical/vertical.py 2006-04-29 17:16:16 UTC (rev 1358) +++ sqlalchemy/branches/schema/examples/vertical/vertical.py 2006-04-29 18:41:13 UTC (rev 1359) </span><span class="lines">@@ -5,7 +5,7 @@ </span><span class="cx"> represented in distinct database rows. This allows objects to be created with dynamically changing </span><span class="cx"> fields that are all persisted in a normalized fashion.""" </span><span class="cx"> </span><del>-e = create_engine('sqlite://', echo=True) </del><ins>+e = BoundMetaData('sqlite://', echo=True) </ins><span class="cx"> </span><span class="cx"> # this table represents Entity objects. each Entity gets a row in this table, </span><span class="cx"> # with a primary key and a title. </span><span class="lines">@@ -48,13 +48,15 @@ </span><span class="cx"> object's _entities dictionary for the appropriate value, and the __setattribute__ </span><span class="cx"> method is overridden to set all non "_" attributes as EntityValues within the </span><span class="cx"> _entities dictionary. """ </span><del>- def __init__(self): - """the constructor sets the "_entities" member to an EntityDict. A mapper - will wrap this property with its own history-list object.""" - self._entities = EntityDict() </del><ins>+ + # establish the type of '_entities' + _entities = EntityDict + </ins><span class="cx"> def __getattr__(self, key): </span><span class="cx"> """getattr proxies requests for attributes which dont 'exist' on the object </span><span class="cx"> to the underying _entities dictionary.""" </span><ins>+ if key[0] == '_': + return super(Entity, self).__getattr__(key) </ins><span class="cx"> try: </span><span class="cx"> return self._entities[key].value </span><span class="cx"> except KeyError: </span><span class="lines">@@ -88,7 +90,10 @@ </span><span class="cx"> the value to the underlying datatype of its EntityField.""" </span><span class="cx"> def __init__(self, key=None, value=None): </span><span class="cx"> if key is not None: </span><del>- self.field = class_mapper(EntityField).get_by(name=key) or EntityField(key) </del><ins>+ sess = create_session() + self.field = sess.query(EntityField).get_by(name=key) or EntityField(key) + # close the session, which will make a loaded EntityField a detached instance + sess.close() </ins><span class="cx"> if self.field.datatype is None: </span><span class="cx"> if isinstance(value, int): </span><span class="cx"> self.field.datatype = 'int' </span><span class="lines">@@ -114,16 +119,17 @@ </span><span class="cx"> mapper( </span><span class="cx"> EntityValue, entity_values, </span><span class="cx"> properties = { </span><del>- 'field' : relation(EntityField, lazy=False) </del><ins>+ 'field' : relation(EntityField, lazy=False, cascade='all') </ins><span class="cx"> } </span><span class="cx"> ) </span><span class="cx"> </span><del>-entitymapper = mapper(Entity, entities, properties = { - '_entities' : relation(EntityValue, lazy=False) </del><ins>+mapper(Entity, entities, properties = { + '_entities' : relation(EntityValue, lazy=False, cascade='save-update') </ins><span class="cx"> }) </span><span class="cx"> </span><span class="cx"> # create two entities. the objects can be used about as regularly as </span><span class="cx"> # any object can. </span><ins>+session = create_session() </ins><span class="cx"> entity = Entity() </span><span class="cx"> entity.title = 'this is the first entity' </span><span class="cx"> entity.name = 'this is the name' </span><span class="lines">@@ -137,14 +143,15 @@ </span><span class="cx"> entity2.data = ('hoo', 'ha') </span><span class="cx"> </span><span class="cx"> # commit </span><del>-objectstore.commit() </del><ins>+[session.save(x) for x in (entity, entity2)] +session.flush() </ins><span class="cx"> </span><span class="cx"> # we would like to illustate loading everything totally clean from </span><del>-# the database, so we clear out the objectstore. -objectstore.clear() </del><ins>+# the database, so we clear out the session +session.clear() </ins><span class="cx"> </span><span class="cx"> # select both objects and print </span><del>-entities = entitymapper.select() </del><ins>+entities = session.query(Entity).select() </ins><span class="cx"> for entity in entities: </span><span class="cx"> print entity.title, entity.name, entity.price, entity.data </span><span class="cx"> </span><span class="lines">@@ -152,13 +159,19 @@ </span><span class="cx"> entities[0].price=90 </span><span class="cx"> entities[0].title = 'another new title' </span><span class="cx"> entities[1].data = {'oof':5,'lala':8} </span><ins>+entity3 = Entity() +entity3.title = 'third entity' +entity3.name = 'new name' +entity3.price = '$1.95' +entity3.data = 'some data' +session.save(entity3) </ins><span class="cx"> </span><span class="cx"> # commit changes. the correct rows are updated, nothing else. </span><del>-objectstore.commit() </del><ins>+session.flush() </ins><span class="cx"> </span><del>-# lets see if that one came through. clear object store, re-select </del><ins>+# lets see if that one came through. clear the session, re-select </ins><span class="cx"> # and print </span><del>-objectstore.clear() -entities = entitymapper.select() </del><ins>+session.clear() +entities = session.query(Entity).select() </ins><span class="cx"> for entity in entities: </span><span class="cx"> print entity.title, entity.name, entity.price, entity.data </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemy__init__py"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/__init__.py (1358 => 1359)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/__init__.py 2006-04-29 17:16:16 UTC (rev 1358) +++ sqlalchemy/branches/schema/lib/sqlalchemy/__init__.py 2006-04-29 18:41:13 UTC (rev 1359) </span><span class="lines">@@ -14,7 +14,7 @@ </span><span class="cx"> from sqlalchemy.orm import * </span><span class="cx"> import sqlalchemy.ext.proxy </span><span class="cx"> </span><del>-from sqlalchemy.orm.session import Session, get_session </del><ins>+from sqlalchemy.orm.session import Session, current_session </ins><span class="cx"> </span><span class="cx"> create_engine = sqlalchemy.engine.create_engine </span><span class="cx"> create_session = sqlalchemy.orm.session.Session </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyattributespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/attributes.py (1358 => 1359)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/attributes.py 2006-04-29 17:16:16 UTC (rev 1358) +++ sqlalchemy/branches/schema/lib/sqlalchemy/attributes.py 2006-04-29 18:41:13 UTC (rev 1359) </span><span class="lines">@@ -37,11 +37,12 @@ </span><span class="cx"> create_prop method on AttributeManger, which can be overridden to provide </span><span class="cx"> subclasses of SmartProperty. </span><span class="cx"> """ </span><del>- def __init__(self, manager, key, uselist, callable_, **kwargs): </del><ins>+ def __init__(self, manager, key, uselist, callable_, typecallable, **kwargs): </ins><span class="cx"> self.manager = manager </span><span class="cx"> self.key = key </span><span class="cx"> self.uselist = uselist </span><span class="cx"> self.callable_ = callable_ </span><ins>+ self.typecallable= typecallable </ins><span class="cx"> self.kwargs = kwargs </span><span class="cx"> def init(self, obj, attrhist=None): </span><span class="cx"> """creates an appropriate ManagedAttribute for the given object and establishes </span><span class="lines">@@ -50,7 +51,7 @@ </span><span class="cx"> func = self.callable_(obj) </span><span class="cx"> else: </span><span class="cx"> func = None </span><del>- return self.manager.create_managed_attribute(obj, self.key, self.uselist, callable_=func, attrdict=attrhist, **self.kwargs) </del><ins>+ return self.manager.create_managed_attribute(obj, self.key, self.uselist, callable_=func, attrdict=attrhist, typecallable=self.typecallable, **self.kwargs) </ins><span class="cx"> def __set__(self, obj, value): </span><span class="cx"> self.manager.set_attribute(obj, self.key, value) </span><span class="cx"> def __delete__(self, obj): </span><span class="lines">@@ -173,7 +174,7 @@ </span><span class="cx"> This is the "list history container" object. </span><span class="cx"> Subclasses util.HistoryArraySet to provide "onchange" event handling as well </span><span class="cx"> as a plugin point for BackrefExtension objects.""" </span><del>- def __init__(self, obj, key, data=None, extension=None, trackparent=False, **kwargs): </del><ins>+ def __init__(self, obj, key, data=None, extension=None, trackparent=False, typecallable=None, **kwargs): </ins><span class="cx"> ManagedAttribute.__init__(self, obj, key) </span><span class="cx"> self.extension = extension </span><span class="cx"> self.trackparent = trackparent </span><span class="lines">@@ -189,10 +190,11 @@ </span><span class="cx"> except KeyError: </span><span class="cx"> if data is not None: </span><span class="cx"> list_ = data </span><ins>+ elif typecallable is not None: + list_ = typecallable() </ins><span class="cx"> else: </span><span class="cx"> list_ = [] </span><del>- obj.__dict__[key] = [] - </del><ins>+ obj.__dict__[key] = list_ </ins><span class="cx"> util.HistoryArraySet.__init__(self, list_, readonly=kwargs.get('readonly', False)) </span><span class="cx"> def do_value_changed(self, obj, key, item, listval, isdelete): </span><span class="cx"> pass </span><span class="lines">@@ -334,16 +336,16 @@ </span><span class="cx"> upon an attribute change of value.""" </span><span class="cx"> pass </span><span class="cx"> </span><del>- def create_prop(self, class_, key, uselist, callable_, **kwargs): </del><ins>+ def create_prop(self, class_, key, uselist, callable_, typecallable, **kwargs): </ins><span class="cx"> """creates a scalar property object, defaulting to SmartProperty, which </span><span class="cx"> will communicate change events back to this AttributeManager.""" </span><del>- return SmartProperty(self, key, uselist, callable_, **kwargs) </del><ins>+ return SmartProperty(self, key, uselist, callable_, typecallable, **kwargs) </ins><span class="cx"> def create_scalar(self, obj, key, **kwargs): </span><span class="cx"> return ScalarAttribute(obj, key, **kwargs) </span><del>- def create_list(self, obj, key, list_, **kwargs): </del><ins>+ def create_list(self, obj, key, list_, typecallable=None, **kwargs): </ins><span class="cx"> """creates a history-aware list property, defaulting to a ListAttribute which </span><span class="cx"> is a subclass of HistoryArrayList.""" </span><del>- return ListAttribute(obj, key, list_, **kwargs) </del><ins>+ return ListAttribute(obj, key, list_, typecallable=typecallable, **kwargs) </ins><span class="cx"> def create_callable(self, obj, key, func, uselist, **kwargs): </span><span class="cx"> """creates a callable container that will invoke a function the first </span><span class="cx"> time an object property is accessed. The return value of the function </span><span class="lines">@@ -491,15 +493,15 @@ </span><span class="cx"> def is_class_managed(self, class_, key): </span><span class="cx"> return hasattr(class_, key) and isinstance(getattr(class_, key), SmartProperty) </span><span class="cx"> </span><del>- def create_managed_attribute(self, obj, key, uselist, callable_=None, attrdict=None, **kwargs): </del><ins>+ def create_managed_attribute(self, obj, key, uselist, callable_=None, attrdict=None, typecallable=None, **kwargs): </ins><span class="cx"> """creates a new ManagedAttribute corresponding to the given attribute key on the </span><span class="cx"> given object instance, and installs it in the attribute dictionary attached to the object.""" </span><span class="cx"> if callable_ is not None: </span><del>- prop = self.create_callable(obj, key, callable_, uselist=uselist, **kwargs) </del><ins>+ prop = self.create_callable(obj, key, callable_, uselist=uselist, typecallable=typecallable, **kwargs) </ins><span class="cx"> elif not uselist: </span><span class="cx"> prop = self.create_scalar(obj, key, **kwargs) </span><span class="cx"> else: </span><del>- prop = self.create_list(obj, key, None, **kwargs) </del><ins>+ prop = self.create_list(obj, key, None, typecallable=typecallable, **kwargs) </ins><span class="cx"> if attrdict is None: </span><span class="cx"> attrdict = self.attribute_history(obj) </span><span class="cx"> attrdict[key] = prop </span><span class="lines">@@ -521,7 +523,8 @@ </span><span class="cx"> if not hasattr(class_, '_attribute_manager'): </span><span class="cx"> class_._attribute_manager = self </span><span class="cx"> class_._managed_attributes = ObjectAttributeGateway() </span><del>- setattr(class_, key, self.create_prop(class_, key, uselist, callable_, **kwargs)) </del><ins>+ typecallable = getattr(class_, key, None) + setattr(class_, key, self.create_prop(class_, key, uselist, callable_, typecallable=typecallable, **kwargs)) </ins><span class="cx"> </span><span class="cx"> managed_attributes = weakref.WeakKeyDictionary() </span><span class="cx"> </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemymodsthreadlocalpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/mods/threadlocal.py (1358 => 1359)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/mods/threadlocal.py 2006-04-29 17:16:16 UTC (rev 1358) +++ sqlalchemy/branches/schema/lib/sqlalchemy/mods/threadlocal.py 2006-04-29 18:41:13 UTC (rev 1359) </span><span class="lines">@@ -112,7 +112,7 @@ </span><span class="cx"> class_.expunge = expunge </span><span class="cx"> def install_plugin(): </span><span class="cx"> reg = util.ScopedRegistry(session.Session) </span><del>- session._default_session = lambda *args, **kwargs: reg() </del><ins>+ session.register_default_session(lambda *args, **kwargs: reg()) </ins><span class="cx"> engine.default_strategy = 'threadlocal' </span><span class="cx"> sqlalchemy.objectstore = Objectstore() </span><span class="cx"> sqlalchemy.assign_mapper = assign_mapper </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyormmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py (1358 => 1359)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py 2006-04-29 17:16:16 UTC (rev 1358) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py 2006-04-29 18:41:13 UTC (rev 1359) </span><span class="lines">@@ -406,15 +406,12 @@ </span><span class="cx"> if kwargs.has_key('_sa_session'): </span><span class="cx"> session = kwargs.pop('_sa_session') </span><span class="cx"> else: </span><del>- session = sessionlib.get_session(self, raiseerror=False) </del><ins>+ session = sessionlib.current_session(self) </ins><span class="cx"> if session is not None: </span><span class="cx"> if not nohist: </span><del>- # register new with the correct session, before the object's - # constructor is called, since further assignments within the - # constructor would otherwise bind it to whatever get_session() is. </del><span class="cx"> session._register_new(self) </span><span class="cx"> else: </span><del>- session._bind_to(self) </del><ins>+ session._register_clean(self) </ins><span class="cx"> if oldinit is not None: </span><span class="cx"> oldinit(self, *args, **kwargs) </span><span class="cx"> # override oldinit, insuring that its not already one of our </span><span class="lines">@@ -839,7 +836,7 @@ </span><span class="cx"> # in order to save on KeyErrors later on </span><span class="cx"> sessionlib.global_attributes.init_attr(obj) </span><span class="cx"> </span><del>- session._bind_to(obj) </del><ins>+ session._register_clean(obj) </ins><span class="cx"> return obj </span><span class="cx"> </span><span class="cx"> def translate_row(self, tomapper, row): </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyormpropertiespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/properties.py (1358 => 1359)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/properties.py 2006-04-29 17:16:16 UTC (rev 1358) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/properties.py 2006-04-29 18:41:13 UTC (rev 1359) </span><span class="lines">@@ -75,7 +75,7 @@ </span><span class="cx"> if not self.parent.is_assigned(instance): </span><span class="cx"> return object_mapper(instance).props[self.key].setup_loader(instance) </span><span class="cx"> def lazyload(): </span><del>- session = sessionlib.get_session(instance) </del><ins>+ session = sessionlib.object_session(instance) </ins><span class="cx"> connection = session.connection(self.parent) </span><span class="cx"> clause = sql.and_() </span><span class="cx"> try: </span><span class="lines">@@ -210,7 +210,7 @@ </span><span class="cx"> else: </span><span class="cx"> if self.primaryjoin is None: </span><span class="cx"> self.primaryjoin = sql.join(parent.unjoined_table, self.target).onclause </span><del>- print "PJ", self.primaryjoin </del><ins>+ </ins><span class="cx"> # if the foreign key wasnt specified and theres no assocaition table, try to figure </span><span class="cx"> # out who is dependent on who. we dont need all the foreign keys represented in the join, </span><span class="cx"> # just one of them. </span><span class="lines">@@ -373,7 +373,7 @@ </span><span class="cx"> def lazyload(): </span><span class="cx"> params = {} </span><span class="cx"> allparams = True </span><del>- session = sessionlib.get_session(instance, raiseerror=False) </del><ins>+ session = sessionlib.object_session(instance) </ins><span class="cx"> #print "setting up loader, lazywhere", str(self.lazywhere), "binds", self.lazybinds </span><span class="cx"> if session is not None: </span><span class="cx"> for col, bind in self.lazybinds.iteritems(): </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyormquerypy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/query.py (1358 => 1359)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/query.py 2006-04-29 17:16:16 UTC (rev 1358) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/query.py 2006-04-29 18:41:13 UTC (rev 1359) </span><span class="lines">@@ -28,7 +28,7 @@ </span><span class="cx"> self._get_clause = self.mapper._get_clause </span><span class="cx"> def _get_session(self): </span><span class="cx"> if self._session is None: </span><del>- return sessionlib.get_session() </del><ins>+ return sessionlib.required_current_session() </ins><span class="cx"> else: </span><span class="cx"> return self._session </span><span class="cx"> table = property(lambda s:s.mapper.select_table) </span><span class="lines">@@ -40,9 +40,19 @@ </span><span class="cx"> if not found. The *ident argument is a </span><span class="cx"> list of primary key columns in the order of the table def's primary key columns.""" </span><span class="cx"> key = self.mapper.identity_key(*ident) </span><del>- print "key: " + repr(key) + " ident: " + repr(ident) </del><span class="cx"> return self._get(key, ident, **kwargs) </span><span class="cx"> </span><ins>+ def load(self, *ident, **kwargs): + """returns an instance of the object based on the given identifier. If not found, + raises an exception. The method will *remove all pending changes* to the object + already existing in the Session. The *ident argument is a + list of primary key columns in the order of the table def's primary key columns.""" + key = self.mapper.identity_key(*ident) + instance = self._get(key, ident, reload=True, **kwargs) + if instance is None: + raise exceptions.InvalidRequestError("No instance found for identity %s" % repr(ident)) + return instance + </ins><span class="cx"> def get_by(self, *args, **params): </span><span class="cx"> """returns a single object instance based on the given key/value criterion. </span><span class="cx"> this is either the first value in the result list, or None if the list is </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyormsessionpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/session.py (1358 => 1359)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/session.py 2006-04-29 17:16:16 UTC (rev 1358) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/session.py 2006-04-29 18:41:13 UTC (rev 1359) </span><span class="lines">@@ -60,7 +60,7 @@ </span><span class="cx"> if import_session is not None: </span><span class="cx"> self.uow = unitofwork.UnitOfWork(identity_map=import_session.uow.identity_map) </span><span class="cx"> elif new_imap is False: </span><del>- self.uow = unitofwork.UnitOfWork(identity_map=objectstore.get_session().uow.identity_map) </del><ins>+ self.uow = unitofwork.UnitOfWork(identity_map=current_session().uow.identity_map) </ins><span class="cx"> else: </span><span class="cx"> self.uow = unitofwork.UnitOfWork() </span><span class="cx"> </span><span class="lines">@@ -106,13 +106,21 @@ </span><span class="cx"> method will release the resources of the underlying Connection, otherwise its a no-op. </span><span class="cx"> """ </span><span class="cx"> return self.connection(mapper, close_with_result=True).execute(clause, params, **kwargs) </span><ins>+ </ins><span class="cx"> def close(self): </span><span class="cx"> """closes this Session. </span><del>- - TODO: what should we do here ? </del><span class="cx"> """ </span><ins>+ self.clear() </ins><span class="cx"> if self.transaction is not None: </span><span class="cx"> self.transaction.close() </span><ins>+ + def clear(self): + """removes all object instances from this Session. this is equivalent to calling expunge() for all + objects in this Session.""" + for instance in self: + self._unattach(instance) + self.uow = unitofwork.UnitOfWork() + </ins><span class="cx"> def mapper(self, class_, entity_name=None): </span><span class="cx"> """given an Class, returns the primary Mapper responsible for persisting it""" </span><span class="cx"> return class_mapper(class_, entity_name = entity_name) </span><span class="lines">@@ -213,14 +221,25 @@ </span><span class="cx"> self.uow.flush(self, objects) </span><span class="cx"> </span><span class="cx"> def get(self, class_, *ident, **kwargs): </span><del>- """given a class and a primary key identifier, loads the corresponding object.""" </del><ins>+ """returns an instance of the object based on the given identifier, or None + if not found. The *ident argument is a list of primary key columns in the order of the + table def's primary key columns. + + the entity_name keyword argument may also be specified which further qualifies the underlying + Mapper used to perform the query.""" </ins><span class="cx"> entity_name = kwargs.get('entity_name', None) </span><span class="cx"> return self.query(class_, entity_name=entity_name).get(*ident) </span><span class="cx"> </span><span class="cx"> def load(self, class_, *ident, **kwargs): </span><del>- """given a class and a primary key identifier, loads the corresponding object.""" </del><ins>+ """returns an instance of the object based on the given identifier. If not found, + raises an exception. The method will *remove all pending changes* to the object + already existing in the Session. The *ident argument is a + list of primary key columns in the order of the table def's primary key columns. + + the entity_name keyword argument may also be specified which further qualifies the underlying + Mapper used to perform the query.""" </ins><span class="cx"> entity_name = kwargs.get('entity_name', None) </span><del>- return self.query(class_, entity_name=entity_name).get(*ident) </del><ins>+ return self.query(class_, entity_name=entity_name).load(*ident) </ins><span class="cx"> </span><span class="cx"> def refresh(self, object): </span><span class="cx"> """reloads the attributes for the given object from the database, clears </span><span class="lines">@@ -237,10 +256,12 @@ </span><span class="cx"> self.uow.expunge(object) </span><span class="cx"> </span><span class="cx"> def save(self, object, entity_name=None): </span><del>- """adds an unsaved object to this Session. </del><ins>+ """ + Adds a transient (unsaved) instance to this Session. This operation cascades the "save_or_update" + method to associated instances if the relation is mapped with cascade="save-update". </ins><span class="cx"> </span><del>- The 'entity_name' keyword argument can also be given which will be assigned - to the instances if given. </del><ins>+ The 'entity_name' keyword argument will further qualify the specific Mapper used to handle this + instance. </ins><span class="cx"> """ </span><span class="cx"> for c in object_mapper(object, entity_name=entity_name).cascade_iterator('save-update', object): </span><span class="cx"> if c is object: </span><span class="lines">@@ -249,6 +270,11 @@ </span><span class="cx"> self.save_or_update(c, entity_name=entity_name) </span><span class="cx"> </span><span class="cx"> def update(self, object, entity_name=None): </span><ins>+ """Brings the given detached (saved) instance. into this Session. + If there is a persistent instance with the same identifier (i.e. a saved instance already associated with this + Session), an exception is thrown. + This operation cascades the "save_or_update" method to associated instances if the relation is mapped + with cascade="save-update".""" </ins><span class="cx"> for c in object_mapper(object, entity_name=entity_name).cascade_iterator('save-update', object): </span><span class="cx"> if c is o: </span><span class="cx"> self._update_impl(c, entity_name=entity_name) </span><span class="lines">@@ -263,11 +289,6 @@ </span><span class="cx"> else: </span><span class="cx"> self._update_impl(c, entity_name=entity_name) </span><span class="cx"> </span><del>- def clear(self): - """removes all object instances from this Session. this is equivalent to calling expunge() for all - objects in this Session.""" - self.uow = unitofwork.UnitOfWork() - </del><span class="cx"> def delete(self, object, entity_name=None): </span><span class="cx"> for c in object_mapper(object, entity_name=entity_name).cascade_iterator('delete', object): </span><span class="cx"> self.uow.register_deleted(c) </span><span class="lines">@@ -281,7 +302,7 @@ </span><span class="cx"> ident = mapper.identity(object) </span><span class="cx"> for k in ident: </span><span class="cx"> if k is None: </span><del>- raise InvalidRequestError("Instance '%s' does not have a full set of identity values, and does not represent a saved entity in the database. Use the add() method to add unsaved instances to this Session." % str(obj)) </del><ins>+ raise InvalidRequestError("Instance '%s' does not have a full set of identity values, and does not represent a saved entity in the database. Use the add() method to add unsaved instances to this Session." % repr(obj)) </ins><span class="cx"> key = mapper.identity_key(*ident) </span><span class="cx"> u = self.uow </span><span class="cx"> if u.identity_map.has_key(key): </span><span class="lines">@@ -297,7 +318,7 @@ </span><span class="cx"> def _save_impl(self, object, **kwargs): </span><span class="cx"> if hasattr(object, '_instance_key'): </span><span class="cx"> if not self.uow.has_key(object._instance_key): </span><del>- raise InvalidRequestError("Instance '%s' attached to a different Session" % repr(object)) </del><ins>+ raise InvalidRequestError("Instance '%s' is already persistent in a different Session" % repr(object)) </ins><span class="cx"> else: </span><span class="cx"> entity_name = kwargs.get('entity_name', None) </span><span class="cx"> if entity_name is not None: </span><span class="lines">@@ -306,7 +327,7 @@ </span><span class="cx"> self._register_new(object) </span><span class="cx"> </span><span class="cx"> def _update_impl(self, object, **kwargs): </span><del>- if self._is_bound(object) and object not in self.deleted: </del><ins>+ if self._is_attached(object) and object not in self.deleted: </ins><span class="cx"> return </span><span class="cx"> if not hasattr(object, '_instance_key'): </span><span class="cx"> raise InvalidRequestError("Instance '%s' is not persisted" % repr(object)) </span><span class="lines">@@ -316,25 +337,28 @@ </span><span class="cx"> self._register_clean(object) </span><span class="cx"> </span><span class="cx"> def _register_new(self, obj): </span><del>- self._bind_to(obj) </del><ins>+ self._attach(obj) </ins><span class="cx"> self.uow.register_new(obj) </span><span class="cx"> def _register_dirty(self, obj): </span><del>- self._bind_to(obj) </del><ins>+ self._attach(obj) </ins><span class="cx"> self.uow.register_dirty(obj) </span><span class="cx"> def _register_clean(self, obj): </span><del>- self._bind_to(obj) </del><ins>+ self._attach(obj) </ins><span class="cx"> self.uow.register_clean(obj) </span><span class="cx"> def _register_deleted(self, obj): </span><del>- self._bind_to(obj) </del><ins>+ self._attach(obj) </ins><span class="cx"> self.uow.register_deleted(obj) </span><span class="cx"> </span><del>- def _bind_to(self, obj): - """given an object, binds it to this session. """ </del><ins>+ def _attach(self, obj): + """given an object, attaches it to this session. """ </ins><span class="cx"> if getattr(obj, '_sa_session_id', None) != self.hash_key: </span><span class="cx"> old = getattr(obj, '_sa_session_id', None) </span><del>- # remove from old session. we do this gingerly since _sessions is a WeakValueDict - # and it might be affected by other threads </del><span class="cx"> if old is not None: </span><ins>+ raise InvalidRequestError("Object '%s' is already attached to session '%s'" % (repr(obj), old)) + + # auto-removal from the old session is disabled. but if we decide to + # turn it back on, do it as below: gingerly since _sessions is a WeakValueDict + # and it might be affected by other threads </ins><span class="cx"> try: </span><span class="cx"> sess = _sessions[old] </span><span class="cx"> except KeyError: </span><span class="lines">@@ -345,11 +369,18 @@ </span><span class="cx"> if key is not None: </span><span class="cx"> self.identity_map[key] = obj </span><span class="cx"> obj._sa_session_id = self.hash_key </span><del>- def _is_bound(self, obj): </del><ins>+ + def _unattach(self, obj): + if not self._is_attached(obj): #getattr(obj, '_sa_session_id', None) != self.hash_key: + raise InvalidRequestError("Object '%s' is not attached to this Session" % repr(obj)) + del obj._sa_session_id + + def _is_attached(self, obj): </ins><span class="cx"> return getattr(obj, '_sa_session_id', None) == self.hash_key </span><span class="cx"> def __contains__(self, obj): </span><del>- return self._is_bound(obj) and (obj in self.uow.new or self.uow.has_key(obj._instance_key)) - </del><ins>+ return self._is_attached(obj) and (obj in self.uow.new or self.uow.has_key(obj._instance_key)) + def __iter__(self): + return iter(self.uow.identity_map.values()) </ins><span class="cx"> def _get(self, key): </span><span class="cx"> return self.uow._get(key) </span><span class="cx"> def has_key(self, key): </span><span class="lines">@@ -392,37 +423,40 @@ </span><span class="cx"> # actual Session object directly to the object instance. </span><span class="cx"> _sessions = weakref.WeakValueDictionary() </span><span class="cx"> </span><del>-def get_session(obj=None, raiseerror=True): - """returns the Session corrseponding to the given object instance. By default, if the object is not bound - to any Session, then an error is raised (or None is returned if raiseerror=False). This behavior can be changed - using the "threadlocal" mod, which will add an additional step to return a Session that is bound to the current - thread.""" - if obj is not None: - # does it have a hash key ? - hashkey = getattr(obj, '_sa_session_id', None) - if hashkey is not None: - # ok, return that - try: - return _sessions[hashkey] - except KeyError: - if raiseerror: - raise InvalidRequestError("Session '%s' referenced by object '%s' no longer exists" % (hashkey, repr(obj))) - else: - return None - - return _default_session(obj=obj, raiseerror=raiseerror) </del><ins>+def current_session(obj=None): + if hasattr(obj.__class__, '__sessioncontext__'): + return obj.__class__.__sessioncontext__() + else: + return _default_session(obj=obj) + +# deprecated +get_session=current_session </ins><span class="cx"> </span><del>-def _default_session(obj=None, raiseerror=True): - if obj is None: - if raiseerror: - raise InvalidRequestError("Thread-local Sessions are disabled by default. Use 'import sqlalchemy.mods.threadlocal' to enable.") </del><ins>+def required_current_session(obj=None): + s = current_session(obj) + if s is None: + if obj is None: + raise InvalidRequestError("No global-level Session context is established. Use 'import sqlalchemy.mods.threadlocal' to establish a default thread-local context.") </ins><span class="cx"> else: </span><ins>+ raise InvalidRequestError("No Session context is established for class '%s', and no global-level Session context is established. Use 'import sqlalchemy.mods.threadlocal' to establish a default thread-local context." % (obj.__class__)) + return s + +def _default_session(obj=None): + return None +def register_default_session(callable_): + global _default_session + _default_session = callable_ + +def object_session(obj): + hashkey = getattr(obj, '_sa_session_id', None) + if hashkey is not None: + # ok, return that + try: + return _sessions[hashkey] + except KeyError: </ins><span class="cx"> return None </span><span class="cx"> else: </span><del>- if raiseerror: - raise InvalidRequestError("Object '%s' not bound to any Session" % (repr(obj))) - else: - return None - -unitofwork.get_session = get_session </del><ins>+ return None </ins><span class="cx"> </span><ins>+unitofwork.object_session = object_session + </ins></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyormunitofworkpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/unitofwork.py (1358 => 1359)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/unitofwork.py 2006-04-29 17:16:16 UTC (rev 1358) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/unitofwork.py 2006-04-29 18:41:13 UTC (rev 1359) </span><span class="lines">@@ -43,7 +43,7 @@ </span><span class="cx"> attributes.ListAttribute.__init__(self, obj, key, data=data, **kwargs) </span><span class="cx"> self.cascade = cascade </span><span class="cx"> def do_value_changed(self, obj, key, item, listval, isdelete): </span><del>- sess = get_session(obj, raiseerror=False) </del><ins>+ sess = object_session(obj) </ins><span class="cx"> if sess is not None: </span><span class="cx"> sess._register_dirty(obj) </span><span class="cx"> if self.cascade is not None: </span><span class="lines">@@ -62,7 +62,7 @@ </span><span class="cx"> self.cascade=cascade </span><span class="cx"> def do_value_changed(self, oldvalue, newvalue): </span><span class="cx"> obj = self.obj </span><del>- sess = get_session(obj, raiseerror=False) </del><ins>+ sess = object_session(obj) </ins><span class="cx"> if sess is not None: </span><span class="cx"> sess._register_dirty(obj) </span><span class="cx"> if self.cascade is not None: </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyormutilpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/util.py (1358 => 1359)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/util.py 2006-04-29 17:16:16 UTC (rev 1358) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/util.py 2006-04-29 18:41:13 UTC (rev 1359) </span><span class="lines">@@ -11,10 +11,10 @@ </span><span class="cx"> def __init__(self, arg=""): </span><span class="cx"> values = sets.Set([c.strip() for c in arg.split(',')]) </span><span class="cx"> self.delete_orphan = "delete-orphan" in values </span><del>- self.delete = "delete" in values or self.delete_orphan - self.save_update = "save-update" in values - self.merge = "merge" in values - self.expunge = "expunge" in values </del><ins>+ self.delete = "delete" in values or self.delete_orphan or "all" in values + self.save_update = "save-update" in values or "all" in values + self.merge = "merge" in values or "all" in values + self.expunge = "expunge" in values or "all" in values </ins><span class="cx"> def __contains__(self, item): </span><span class="cx"> return getattr(self, item.replace("-", "_"), False) </span><span class="cx"> </span><span class="cx">\ No newline at end of file </span></span></pre> </div> </div> </body> </html> |