14

我有一个 Django 应用程序,允许用户在其中导入包含联系人数据(会员编号、名字、姓氏等)的 CSV 文件。

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

我的问题是:使用 Django 或直接 Python 实现撤消功能的最佳方法是什么,以便用户可以撤消导入操作并将条记录恢复到原始状态?

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

Table HISTORY
   unique_id
   record_affected_id
   old_value
   new_value

然后,如果用户单击“撤消”,我可以查找与他们的交易关联的 unique_id,并将受该交易影响的每条记录设置为 old_value。

我想知道是否有一种我想念的更简单的方法来做到这一点,或者是否有人有类似的经验。

4

3 回答 3

11

看看django-reversion。它为 Django 模型提供版本控制。可以很容易地添加到现有项目中。

它不采用“当前”指针方法。相反,它会在每次保存对象时对其进行序列化,并将其存储在一个单独的Version模型中,并使用指向该对象的通用外键。(默认情况下,关系字段被序列化为主键。)此外,它允许以灵活的方式将 s 分组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-reversion 得到积极维护,所以不要过多依赖上面的代码,最好查看他们的wiki,了解如何在 django 管理员之外管理版本和修订。例如,已经支持修订注释,这可能会简化一些事情。

于 2010-12-18T20:51:43.500 回答
3

你需要有版本控制,问题不在于 Python 或 Django,而是如何设计数据库来做到这一点。一种常见的方法是存储具有唯一 ID 的文档并跟踪哪个是“当前”。然后撤消只是将“当前”指针放回旧版本的问题。据我所知,这就是你正在做的事情。

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

在 Django 中以通用方式执行此操作可能是一个难题,但如果您让 Django 应用程序以自定义方式支持它会更容易。

然后你会遇到有趣的(非)问题,比如如何在“未来”修订版中编辑内容,然后以一种临时的内容方式一次发布一整套文档。但希望你不需要那个。:-)

于 2010-12-18T19:56:02.620 回答
1

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

于 2010-12-18T20:11:30.130 回答