On Feb 14, 2006, at 12:26 PM, Michael Bayer wrote:
I think the base Session concept needs to be extremely simple, and
I would
rather have it less flexible rather than too complicated. as you
noted,
developers with more sophisticated needs are free to subclass
Session, and
make their own transactional scoping rules by playing around with
UnitOfWork objects. we can even have a collection of "Session"
plugins in
the extensions package, each one designed to meet a different
developer's
idea of how to organize transactions. so a simplistic design is
not going
to hold anyone back, there can be choices.
That sounds like a good strategy.
another point is, with regards to "rollback" there are two completely
distinct rollback concepts in this thing right now, one which is to
just
cancel the UnitOfWork object that has collected "new", "dirty", and
"deleted" items and lists, and the other is a function of the
attributes
system which can "reset" the individual attributes on an object.
While
the former is reasonable, the latter is kind of a curiousity right
now,
and has already confused people (as it is not a true "roll back" of
the
entire in-memory state), so I have pretty much been de-emphasizing
that
little trick and not really documenting it, since its essentially
not very
useful except perhaps for some very specific situation. if you really
want to discard your changes in a unit of work, you should probably
start
all over again from when you created/loaded your initial field of
objects.
Right, that latter type of rollback is just a performance
optimization. I think your approach is sane. Don't bother adding the
"*obj" parameter from Session.rollback if it will cause less confusion.
back to begin/commit, id rather go for something totally simple, like:
def func(s):
# do stuff
s.commit() # commit
# ordinary function calls, they all can commit as they go.
s = objectstore.get_session()
func(s)
# start a transaction,
# functions can call s.commit() but nothing happens
# until the "transaction" is committed
s = objectstore.get_session()
trans = s.begin()
func(s)
trans.commit()
So in simplistic case #1, nobody needs to know anything, its just
s.commit(). functions can commit as they go, or use their own
begin/commits. in less simplistic case #2, a "baton" object is the
ultimate controller of the transaction, and only the outermost caller
needs to know about it, functions can do whatever style of commit they
want. Once you get that initial "trans", all regular calls to
s.commit()
do nothing, nor do any further s.begin() calls...only the trans can
commit
it, thereby ending the block.
That's a good idea (why didn't I think of that? :). Only one other
thing, it should allow something this:
def foo(s):
# do stuff
trans = s.begin()
bar(s) # pass the baton again
trans.commit()
trans = s.begin()
func(s)
trans.commit() # commit everything
That completes the solution--any code can be nested in a session
transaction. Only the outer trans.commit() does anything. Every
session.begin() must be balanced with a trans.commit() (i.e. the
outer trans.commit() would raise an exception if the inner one had
not been committed or rolled back). There should probably be a
trans.rollback() method that just discards all changes since the last
s.begin() or trans.commit()--this could be left out for the time
being (I'm sure it will eventually be requested).
as for everything else, id like to proceed more slowly and wait for
specific developer issues to come in before making too many changes.
Sounds fine with me. I'm a developer and the issues I've been working
on are ones that I can see would be useful for projects that I'm
working on.
also
with regards to the SessionError class, Id like to address "exception"
classes across the entire application in one pass, i.e. create an
exceptions package/hierarchy and nail down the full behavior to be
used
across the whole thing....my exceptions are all string-based right now
simply because i wanted the library to take shape before I could
decide
how exceptions should look.
I understand. However, it's a pain for us developers using
SQLAlchemy. We have to do this type of thing to catch SQLAlchemy-
raised exceptions:
try:
# do some SA stuff
except:
if this is an SA error:
# handle error
else:
raise
All that code will have to be rewritten when SQLAlchemy finally
introduces real exceptions...I'd personally vote for having real
exceptions sooner rather than later. One way to do this is to create
a generic exception class for each module (i.e. MapperError) or even
just one for the whole project (SQLAlchemyError). All exceptions
raised in that module use that exception. Later you can create fine-
grained subclasses for specific situations if needed. No user code
needs to be rewritten unless the fine-grained control is needed. This
takes very little effort on your part and saves a lot of time (and
upgrade pains) for the early adopters off SA.
~ Daniel
-------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc. Do you grep through log files
for problems? Stop! Download the new AJAX search engine that makes
searching your log files as easy as surfing the web. DOWNLOAD SPLUNK!
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=103432&bid=230486&dat=121642
_______________________________________________
Sqlalchemy-users mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/sqlalchemy-users