Ignoring the bug in RevisionMiddleware for now...
So the way the admin site currently handles rollback is:
- Create a new DB savepoint.
- Create a new revision context
- Roll back the revision in the DB, including all model signals
- If the user chooses to roll back the revision, the savepoint created in (1) is committed. Else, the savepoint created in (1) is rolled back.
In order for stage 3 to work, all the DB signals HAVE to fire. Otherwise, when the savepoint is committed, the app will be in an inconsistent state. However, if the signals cause side-effects that can't be rolled back in the transaction, rolling back the savepoint will cause inconsistencies in the app.
Prior to 1.9, the admin preview view was a hacky patch-up which loaded the previous revision into the admin interface without touching the DB. Unfortunately, it means a lot of common-use cases such as foreign keys and computed properties ended up inconsistent in the admin preview.
So our options are:
- Don't use database transactions - (inconsistent previews in common usage, consistent app always.)
- Use database transactions with signals - (full consistency in previews and app in common usage, but inconsistent app if signals have non-db side-effects).
A way to fix signals which can't be rolled back in a DB transaction is to use the new on_commit Django hook, and only apply then when a transaction is completed. That's pretty good practice for non-db side-effects anyway.