2010-12-18 181 views
11

我有一个Django应用程序,允许用户导入包含联系人数据(成员#,名字,姓氏等)的CSV文件。如何使用Python/Django实现“撤消”功能

当它们导入文件时,应用程序检查数据库中的匹配记录,并且:1)如果不存在匹配则插入新记录,或者2)使用新数据更新现有数据。

我的问题是:什么是实现一个撤消功能的最佳方式,使用Django或简单的Python,以便用户可以撤消导入操作,并恢复记录回到原来的状态?

我最初的想法是创建一个表像这样的(伪代码):

Table HISTORY 
    unique_id 
    record_affected_id 
    old_value 
    new_value 

然后,如果用户点击“撤消”我可以查找一个与他们的交易相关联的,并设置每个记录UNIQUE_ID受该交易影响到old_value。

我想知道是否有一个更简单的方法来做到这一点,我错过了,或者如果有人有这样的经验的经验。

回答

10

看看django-reversion。它为Django模型提供版本控制。可以轻松添加到现有项目。

它不采用“当前”指针方法。相反,它在每次保存时序列化对象,并将其存储在单独的Version模型中,并使用指向此对象的泛型外键。 (默认情况下,关系字段序列化为主键。)此外,它允许以灵活的方式将Version s分组到Revision

所以,你可以做这样的事情:

  • 当用户上传CSV,只是保存更改像往常一样,但增加@revision.create_on_success装饰到它执行功能的进口,以便通过所做的任何更改将记录函数将被存储在单个修订版本中。
  • 当用户点击“撤消”时,您只需恢复最新版本。

下面是它如何做::

@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) 

它依赖于您创建的修改用户导入CSV的,只有当事实。这意味着,如果您打算还版本控制其他数据,那么您需要实施某种标志,通过该标志可以获取受最新导入影响的记录。然后您可以通过此标志获取记录,获取最新保存的版本,并还原该版本所属的整个版本。这样的:

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() 

这不是一个漂亮的解决方案,也肯定有办法用这个程序做的更好。我建议看看代码,以更好地理解django-reversion如何工作 - 有很好的文档记录,在没有文档字符串的情况下找不到函数。^_^d

(文档也不错,但竟然是有点误导我,即它们写Version.objects.get_for_date(your_model, date),其中your_model实际上是一个模型实例。)

更新: Django的逆转因此不要依赖上面的代码,并且更好地检查他们的wiki如何管理版本& django的管理员之外的修订。例如,修订注释已经被支持,这可能会简化一些事情。

3

您需要拥有版本控制,并且问题不在于Python或Django,而在于如何设计数据库来执行此操作。一种常见的方式是存储具有唯一ID的文档并追踪哪些是“当前”。撤销然后只是将“当前”指针放回旧版本。就我所知,这就是你在做的事情。

虽然这是做这件事的常见方式,但我不知道它是否最好。我从来没有见过其他方式,这可能意味着它是最好的,或者最好的方式是不明显的。 :-)

在Django中以通用的方式做这件事可能是一个难题,但如果你让Django应用程序以自定义的方式支持它,会更容易。

然后,您将进入乐趣(非)问题,例如如何在“未来”版本中编辑内容,然后一次性发布整组文档,以内容的一种分段方式。但希望你不需要那样。 :-)

1

您的历史记录表看起来很好,除了您不需要new_value字段来执行撤消操作。是的,“如何”撤消“通常是实现的(另一种选择是Lennart的将版本号放入所有记录的方法)。单独的日记表的优点是您不需要在常规查询中处理版本号。