> I want to do the following: commit a change to an object, then commit
> another. Now I want to undo the second change. Easy: get the tid using
> undoLog and call undo(tid, transaction.get()). Now the following
> transactions are in the DB:
> - Change 1
> - Change 2
> - Undo change 2
> Now I want to take the DB back to the state before any of the three
> transactions were commited. I can find no way to do this because all the
> transactions change the same object and therefor I can never undo more
> than the most resent one.
It might help if you showed a specific way you tried. The "obvious" way is
to undo the last 3 transactions at this point. For example:
from ZODB.FileStorage import FileStorage
st = FileStorage("Temp.fs")
db = ZODB.DB(st)
cn = db.open()
rt = cn.root()
rt['state'] = 'start'
rt['state'] = 'two'
rt['state'] = 'three'
print "current state %r" % rt['state'] # prints 'three'
undoable = st.undoLog()
print "after undoing: %r" % rt['state'] # prints 'two'
# Undo the last three (the undo, setting 'three', and
# setting 'two'). Should get back to 'start' then.
undoable = st.undoLog()
for i, desc in enumerate(undoable):
if i > 2:
print "after undoing 3: %r" % rt['state'] # prints 'start'
Output from running that:
current state 'three'
after undoing: 'two'
after undoing 3: 'start'
> It seems to me reasonable that with this state ZODB should be able to
> undo "Change 1" because it is actually in the same state as it was
> between change 1 and change 2.
> I tried undoing them all in one transaction in hopes that the system
> would smart enough to know that it would not cause a conflict, but no
As above, it worked for me. BTW, I was using ZODB 3.4 there, but it
shouldn't matter except for spelling details.
> The reason for all this is that I want to implement standard GUI-style
> undo/redo features in a ZODB application.
> Probably the easiest way to provide this functionality would be to
> implement a kind of undo that undoes all transactions back to a given
My advice is to implement undo/redo in your application, not to rely on ZODB
for it. Some storages don't support undo at all, while those that do lose
old state after packing (or lose old state all by themselves). In addition,
it's very easy to create inconsistent database state by mucking with
DB-level undo unless undos are treated strictly in stack-like fashion.
> Also in the back of my mind is the idea of being able to configure a
> connection to give me a non-current view of the DB (using MVCC). This
> would allow me to implement suffisticated diff'ing algorithms between
> states of the DB. This seems like it would be very easy to implement.
> With MVCC in place and all.
It's not an intended use case for MVCC, and there's no direct support for
that now. Don't know how hard it would be to add; there are no current
plans to do so.
> And would allow applications to implement much more suffisticates undo
> than could be implemented in the current system. Thoughts?
ZODB isn't intended to be a version control system <0.6 wink>. Some
storages support undo(), but it's intended to be used lightly at worst --
the implementation isn't warped toward doing undo efficiently. Seems to me
it's been treated as a necessary convenience, but no more than that.
> and is this what custom conflict resolution is for?
Not specifically, no. There is no general conflict resolution, BTW: all
conflict resolution is "custom". A type can choose to implement conflict
resolution if the author judges that it's OK for two (or more) transactions
to mutate the same piece of state simultaneously. Whether that makes any
sense at all, and the precise sense it may make, are entirely up to the
conflict resolution method's author. Undo isn't special here, it's just
another way of mutating state.
For more information about ZODB, see the ZODB Wiki:
ZODB-Dev mailing list - ZODB-Dev@zope.org