14

ユーザーが連絡先データ (メンバーシップ番号、名、姓など) を含む CSV ファイルをインポートできるようにする Django アプリケーションがあります。

ファイルをインポートすると、アプリケーションはデータベースで一致するレコードをチェックし、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このオブジェクトを指す一般的な外部キーを使用して別のモデルに格納します。(リレーションシップ フィールドは、デフォルトで主キーとしてシリアル化されます。) また、柔軟な方法でVersions を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 は積極的に維持されているため、上記のコードにあまり依存しないでください。django の管理者以外でバージョンとリビジョンを管理する方法については、 wikiを確認してください。たとえば、リビジョン コメントは既にサポートされているため、少し簡単になるかもしれません。

于 2010-12-18T20:51:43.500 に答える
3

バージョン管理が必要です。問題は Python や Django ではなく、これを行うためのデータベースの設計方法です。一般的な方法の 1 つは、ドキュメントを一意の ID で保存し、どれが「最新」であるかを追跡することです。元に戻すには、「現在の」ポインタを古いリビジョンに戻すだけです。これは、私が見る限り、あなたがしていることです。

これが一般的な方法ですが、それが最善かどうかはわかりません。私は他の方法を見たことがありません。:-)

Django で一般的な方法でこれを行うのはおそらく難しい問題ですが、Django アプリで独自の方法でサポートするようにすると、より簡単になります。

次に、「将来の」リビジョンで物事を編集し、ステージングのようなコンテンツの方法でドキュメントのセット全体を一度に公開する方法など、楽しい (ではない) 問題に入ります。しかし、うまくいけば、それは必要ありません。:-)

于 2010-12-18T19:56:02.620 に答える
1

取り消しを実行するために new_value フィールドが必要ないことを除いて、履歴テーブルは問題ないように見えます。はい、それが「元に戻す」がしばしば実装される方法です (他の代替手段は、すべてのレコードにバージョン番号を入れるという Lennart のアプローチです)。別のジャーナル テーブルの利点は、通常のクエリでバージョン番号を処理する必要がないことです。

于 2010-12-18T20:11:30.130 に答える