簡體   English   中英

如何使用Python / Django實現“撤消”功能

[英]How to implement an “undo” feature using Python/Django

我有一個Django應用程序,我允許用戶導入帶有聯系人數據的CSV文件(成員資格#,名字,姓氏等)。

當他們導入文件時,應用程序會檢查數據庫是否有匹配的記錄,並且:1)如果不存在匹配則插入新記錄,或者2)使用新數據更新現有數據。

我的問題是:使用Django或直接Python實現撤銷功能的最佳方法是什么,以便用戶可以撤消導入操作並將多個記錄恢復到原始狀態?

我最初的想法是創建一個這樣的表(偽代碼):

Table HISTORY
   unique_id
   record_affected_id
   old_value
   new_value

然后,如果用戶單擊“撤消”,我可以查找與其事務關聯的unique_id,並將受該事務影響的每條記錄設置為old_value。

我想知道是否有一種更簡單的方法可以做到這一點,我錯過了,或者如果有人有這樣的經歷。

看看django-reversion 它為Django模型提供版本控制。 可以輕松添加到現有項目中。

它不采用“當前”指針方法。 相反,它在每次保存對象時將對象序列化,並將其存儲在單獨的Version模型中,並使用指向此對象的通用外鍵。 (關系字段被序列化為默認主鍵。)另外,它允許組Version s轉換Revision S IN的靈活方式。

所以你可以這樣做:

  • 當用戶上傳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如何工作 - 記錄得很好,找不到沒有docstring的函數。 ^ _ ^ d

(文檔也很好,但結果對我來說有點誤導,即他們寫了Version.objects.get_for_date(your_model, date) ,其中your_model實際上是一個模型實例。)

更新: django-reversion是主動維護的,所以不要依賴上面的代碼,並且更好地檢查他們的wiki如何管理django管理員之外的版本和修訂版。 例如,已經支持修訂注釋,這可能會簡化一些事情。

你需要有版本控制,問題不是Python或Django,而是如何設計數據庫來做到這一點。 一種常見的方法是存儲具有唯一ID的文檔並跟蹤哪個是“當前”。 然后撤銷只是將“當前”指針放回舊版本。 據我所知,這是你在做什么。

雖然這是常見的做法,但我不知道它是否是最好的。 我從未見過任何其他方式,這可能意味着它是最好的,或者最好的方式是不明顯的。 :-)

在Django中以通用方式執行此操作可能是一個難題,但如果您使Django應用程序以自定義方式支持它會更容易。

然后你會進入Fun(not)問題,比如如何編輯“未來”修訂版中的內容,然后一次性地發布一整套文檔。 但希望你不需要那個。 :-)

您的歷史記錄表看起來很好,除了您不需要new_value字段來執行撤消。 是的,經常實施“如何”撤銷“(另一種選擇是Lennart將版本號放入所有記錄的方法)。 單獨的日志表的優點是您不需要在常規查詢中處理版本號。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM