On May 10, 2011, at 12:48 PM, Luper Rouch wrote: > I often run into problems when working with SA's events. > MapperExtension's methods are called in the middle of a flush, and so > there are things to take care of. For example: > > * if you add new objects to the session, or do an update during an > event, you'll have to call session.flush() a second time for them to > be actually inserted or updated (and you can't call flush() within the > event since you're in the middle of a flush). There is a workaround > for this issue, you use a SessionExtension that looks for dirty > objects in its after_flush_postexec() method, and call flush() a > second time if there is any [1]. > > * if an error occurs during an event (e.g. a database timeout), you > can't handle it within the event with a rollback or else you'll get an > "InvalidRequestError: The transaction is inactive due to a rollback in > a subtransaction. Issue rollback() to cancel the transaction." error > on the next usage of the transaction [2]. > > While the first problem just involves a quick hack (which you still > have to figure out), the second is more annoying and involves coupling > (you have to know that the particular table you are flushing triggers > events and needs special error handling). > > I think it would make sense to add new events triggered *outside* of > the flush mechanism. For example the order of operations for an update > would look something like this : > > outer_before_update() > [begin] > before_update() > [emit SQL update] > after_update() > [commit] > outer_after_update() > > This way you would code normally in events, as you do in the rest of > your code, without taking care of special rules. > > Maybe MapperExtension is not the right place to add these kind of > events (as I see it, it's more a tool to extend the API than a day to > day tool to work with events), and something like MapperEvents should > be created instead ?
You should be using SessionExtension for most of these use cases. As the docs state you can't change the flush plan inside of before_update()/before_insert() kinds of events, you do that in before_flush(). There's a number of examples in examples/ and on the wiki which illustrate this usage. Also you should probably be moving up to 0.7 where the events system has been vastly improved. In particular there is an ample space for us to add new functionality to listen() that will allow the "check the dirty list" part of session event handling, and similar, to be automated by SQLAlchemy itself. The session doesn't support the "try something, it fails, rollback, keep doing the flush" kind of pattern. There's info in the FAQ regarding the rationale here. You'll have to approach that in a different way, typically using begin_nested() to work with SAVEPOINT and working outside the scope of flush(). This is something that should be dealt with at the point of integration with your app and not within the Session itself (or at least, a custom Session subclass). A "database timeout" though is outside the scope of all of that - if you lose your connection, your transaction is gone. You need to run the whole operation again from the start. Typically its better to prevent timeouts from the start (depends on why you think you'd be getting timeouts) or to take an optimistic approach (i.e. failures are rare, they happen, the app recovers on the next try). The purpose of events is to provide hooks into points inside of SQLAlchemy's internal processes that would otherwise be impossible. Adding events outside of its internal processes just to suit various use cases begins to turn it into a framework, which is not what SQLAlchemy is. -- You received this message because you are subscribed to the Google Groups "sqlalchemy" group. To post to this group, send email to [email protected]. To unsubscribe from this group, send email to [email protected]. For more options, visit this group at http://groups.google.com/group/sqlalchemy?hl=en.
