Martijn Faassen wrote:
Laurence Rowe wrote:
Martijn Faassen wrote:
[snip]
Before we talk more about session configuration, please explain why
we're not talking about engine configuration. :)
Engine configuration is a subset of session configuration. You cannot
have a single ScopedSession for a package if you want to have
multiple instances of that application.
I don't understand then what Mike Bayer wrote before:
Mike Bayer wrote:
If you are running different instances each connected to a different
engine within one process you wouldn't need any awareness of engines
at the object level (therefore no entity_name) and also no engine
proxying - you should have separate Session instances for each
"process" managed by scoped_session(), which was designed to handle
this. Multiple apps on one codebase within one process was an
original requirement of Pylons as well, though nobody has ever used
it.
That seemed to suggest to me that scoped sessions were an appropriate
solution.
Anyway, back to you:
[snip]
> I'm not sure whether it would be a good idea to wrap this in a session
> property, or just register it as an adapter. The only other object that
> would need access to the session (either as a property or through
> adaption) would be the application instance root object. Something like:
>
> @adapter(MyApp)
> @provider(ISession)
> def root_session(context):
> return context._sessioncontext()
This looks to me like it could be a simple function that looks up a
local utility instead:
def session():
return component.getUtility(ISessionContext)()
We get the right context from the site that way. I don't see the point
in trying to re-implement local utilities with adapters while
zope.component already does something quite similar for you. That said,
I still have hope we can used ScopedSession and forgo a utility lookup
here, see below...
> And the simplest persistent session context might be:
>
> class PersistentSessionContext(persistent):
> implements(ISessionContext)
>
> def __init__(self, url, twophase=False, engine_kw={}, session_kw={}):
> self.url = url
> self.twophase = twophase
> self.engine_kw = engine_kw
> self.session_kw = session_kw
>
> def __call__(self):
> session = getattr(self._v_session, None)
> if session is None:
> engine = getattr(self._v_engine, None)
> if engine is None:
> engine = self._v_engine = create_engine(
> self.url, **self.engine_kw)
> session = self._v_session = create_session(
> bind=engine, twophase=self.twophase, **self.session_kw)
> return session
Doesn't ScopedSession already take care of this using ScopedRegistry?
>
Perhaps a more clever scopefunc is necessary to introduce a per-instance
scope? Right now it's only per-thread scope. scopefunc could also do a
local utility lookup that gets it a way to uniquely identify the current
application (not sure what would be best, object id? zodb id? a unique
string generated for each installed application?).
Something like this:
def scopefunc():
return (component.getUtility(ISessionSiteScope).applicationScope(),
thread.getindent())
Session = scoped_session(sessionmaker(autoflush=True), scopefunc)
> A more complex scheme might maintain a dict of ScopedSessions keyed by
> application path, outside of the object cache. You could also ensure
> that only a single engine is created for a given set of arguments, but
> this seems overkill
> Everything would then get a session consistently with a call to
> ISession(self) or ISession(self.context) in the case of views. No parent
> pointers involved.
I still don't understand why this is nicer than a local utility lookup.
I understand the two cooperating applications use case, but that can be
easily provided for with using setSite(), just like you *already* need
to do to make everything work right with the other local utilities that
this application might be using. Above in my scopefunc example I assume
that setSite has been set up appropriately.
That would be fine if you had the same configuration across all sessions
(e.g. they all connected to the same engine / database) or each session
was configured at the start of every request. Presumably we will want to
connect different application instances to different databases.
This means that if we have a central register of ScopedSessions, then we
must key it by some unique application id (path I guess).
def application_session(app):
try:
return registry[app.getPath()]()
except KeyError:
return registry.setdefault(app.getPath(),
scoped_session(sessionmaker(**app.getConfiguration())))()
My point about using adapters, or indeed properties to access the
session, is that the only object needing to access the session which
cannot look it up directly with Session.object_session(object) is the
application root object. To me it seems simpler to do this than to
register utilities. Also it would be nice to have a consistent way to
lookup the session.
If you don't use the ZODB at all, you could still set up local sites
(I'm not sure how hard this is, but it *should* be possible;
zope.component has no knowledge of persistence), or if it's just one app
per zope instance, set up a global ISessionSiteScope utility.
> We do still need to setup parent pointers though for grok.url and
> IAbsoluteURL to work. It looks fairly easy to add location information
> to the containers themselves:
(note that grok.url uses IAbsoluteURL, so we just care about IAbsoluteURL)
So far this isn't a particular problem in Grok as traversal uses
zope.location.located() to locate everything. That said, if you want to
directly access an object by accessing ORM-mapped attributes and then
get a URL for that object, this won't work. Since it *does* work when
you use the ZODB, it'd be nice if it also worked properly with sqlalchemy.
Hopefully we can indeed make containers behave the right way by making
our own MappedCollection.
>
> from sqlalchemy.orm.collections import MappedCollection,
collection_adapter
>
> class LocatedCollection(MappedCollection):
>
> @property
> def __name__(self):
> return collection_adapter(self).attr.key
>
> @property
> def __parent__(self):
> return collection_adapter(self).owner_state.obj()
>
> But locating mapped objects themselves is more complex, they could
> exist in more than one collection at once. Perhaps A LocationProxy
> could solve this.
LocationProxy (using zope.location.located()) has worked quite well for
me so far.
That sounds promising then.
Laurence
_______________________________________________
Zope-Dev maillist - Zope-Dev@zope.org
http://mail.zope.org/mailman/listinfo/zope-dev
** No cross posts or HTML encoding! **
(Related lists -
http://mail.zope.org/mailman/listinfo/zope-announce
http://mail.zope.org/mailman/listinfo/zope )