[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