Take a look at django-reversion
. It provides version control for Django models. Can be easily added to existing project.
It doesn't employ "current" pointer approach. Instead, it serializes object each time it's being saved and stores it in a separate Version
model with generic foreign key pointing to this object. (Relationship fields are serialized as primary keys by default.) Also, it allows to group Version
s into Revision
s in a flexible way.
So you can do something like that:
- When user uploads CSV, just save changes as usual, but add
@revision.create_on_success
decorator to the function which does the import—so that any changes to records made by that function will be stored under a single revision.
- When user hits "Undo", you just revert the latest revision.
Here's how it could be done::
@revision.create_on_success
def import_csv(request, csv):
# Old versions of all objects save()d here will
# belong to single revision.
def undo_last_csv_import(request):
# First, get latest revision saved by this user.
# (Assuming you create revisions only when user imports a CSV
# and do not version control other data.)
revision = Revision.objects.filter(user=request.user)\
.order_by('-date_created')[0]
# And revert it, delete=True means we want to delete
# any newly added records as well
revision.revert(delete=True)
It relies on the fact that you create revisions only when user imports CSVs. That means, if you plan to also version control other data, then you'll need to implement some kind of a flag by which you can get records affected by the latest import. Then you can get a record by this flag, get it latest saved version, and revert the whole revision that version belongs to. Like this::
def undo_last_csv_import(request):
some_record = Record.objects.by_user(request.user).from_the_last_import()[0]
latest_saved_version_of_some_record = Version.objects.get_for_date(
some_record,
datetime.now(), # The latest saved Version at the moment.
)
# Revert all versions that belong to the same revision
# as the version we got above.
latest_saved_version_of_some_record.revision.revert()
It's not a beautiful solution, there most certainly are ways to do it better with this app. I recommend to take a look at the code to understand better how does django-reversion
work—very well documented, couldn't find a function without a docstring. ^_^d
(Documentation is also good, but turned out to be a bit misleading for me, i.e. they write Version.objects.get_for_date(your_model, date)
, where your_model is actually a model instance.)
Update: django-reversion is actively maintained, so don't rely on the code above much, and better check their wiki on how to manage versions & revisions outside django's admin. For instance, revision comments are already supported, that may simplify things a bit.