[Zope-dev] Re: zope.sqlalchemy

Michael Bayer mike_mp at zzzcomputing.com
Tue May 6 12:53:05 EDT 2008


On May 6, 2008, at 12:14 PM, Laurence Rowe wrote:

> Martijn Faassen wrote:
>> One thing I understood from Christian Theune is that with scoped  
>> sessions, explicit session.save() is not always necessary. Since I  
>> see it being used here, could you perhaps comment on this?
>
> Registering a mapper with Session.mapper would work with this  
> extension, but I'm reluctant to recommend it for two reasons: I  
> don't know how it works with the declarative plugin; and it  
> necessarily limits mapped classes to a single Session and therefor a  
> single engine. In a zope context I think it's quite likely that you  
> could have the same classes mapped to different databases (i.e. two  
> instances of a single application).

hi there -

a little background on the "save_on_init" option of Session.mapper.    
This behavior has its roots way back in SQLAlchemy 0.1, when there was  
no Session or Query or anything like that, and objects, when  
instantiated, went directly to a thread-local registry  
automatically.   When SQLA 0.2 came out, we introduced all the  
additional constructs like Session and such which are familiar today,  
but extensions were provided which, when enabled, would re-enable the  
0.1 behavior of "everything threadlocal/automatic" in a similar way.    
Ultimately thats where Session.mapper comes from.

Like all typing-savers, "save on init" by then was used by dozens of  
Pylons users who swore by it and would scream and yell at any hint of  
removing this already legacy feature.  At the same time, new users who  
were using Pylons tutorials (and therefore save_on_init, without  
really knowing it) in conjunction with creating their own Session  
objects were baffled by error messages like "object X is already in  
session Y".

By the time 0.4 came out, we had started automating Session a lot  
more, adding autoflush capability to it.  This feature immediately had  
issues with save_on_init for this reason:

class MyClass(object):
     def __init__(self):
         self.some_variable = session.query(Foobar).filter(xyz).one()

Where above, the query(Foobar) would fire off autoflush, MyClass would  
be flushed, and then an IntegrityError would be raised since MyClass  
would be missing some necessary state.   Changing "save_on_init" to  
fire off *after* __init__ was a possibility there but then other  
things could break.

So I've already not liked save_on_init for a couple of years due to  
its inherent intrusiveness, and because SA historically does not like  
being in the business of providing framework features (though we have  
decided to stay in that arena to some degree with declarative and  
scoped_session).

The "Session.mapper" feature is stressed a whole lot less in the 0.4  
docs, and as I work on the 0.5 docs this week I'm feeling very much  
like I'm going to remove it from the main documentation altogether.  
We''re consolidating the "save/update/save_or_update" names into just  
"add()" and "add_all()", so explicitly adding items to a Session  
should be a more pleasant experience which I wouldn't want anyone to  
miss.

The aspect of Session.mapper which is somewhat reduntant vs.  
declarative is that they both want to add an automatic  
__init__(**kwargs) method which assigns all given keyword values to  
the instance.    They are not incompatible because Session.mapper only  
adds an __init__ if none is available already.

The final feature of Session.mapper which is more reasonable is the  
"query" attribute.   This feature allows you to say:

	MyClass.query

as an equivalent for session.query(MyClass).

For that specific attribute, instead of using Session.mapper, its  
functionality has been exported into its own descriptor-producing  
method, like so:

	class MyBaseClass(object):
	    query = Session.query_property()

So this is a way to get that one aspect without buying into the  
Session.mapper thing.




More information about the Zope-Dev mailing list