2010-12-18 178 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的將版本號放入所有記錄的方法)。單獨的日記表的優點是您不需要在常規查詢中處理版本號。