web-dev-qa-db-ja.com

Django管理者のモデル履歴に結び付ける

セットアップ:

  • Djangoアプリケーションで作業しています。これにより、ユーザーはデータベースにオブジェクトを作成し、必要に応じて戻って編集することができます。
  • Djangoの管理サイトは、管理サイトを通じてオブジェクトに加えられた変更の履歴を保持します。

質問:

  • ユーザーが「コンテンツ」に加えた変更の履歴を表示できるように、管理サイトの変更履歴にアプリケーションをフックするにはどうすればよいですか?
85
akdom

管理履歴は、他のDjangoアプリと同様のアプリですが、管理サイトへの特別な配置は例外です。

モデルはDjango.contrib.admin.models.LogEntryにあります。

ユーザーが変更を加えたら、次のようにログに追加します(contrib/admin/options.pyから恥知らずに盗まれました:

from Django.contrib.admin.models import LogEntry, ADDITION
LogEntry.objects.log_action(
    user_id         = request.user.pk, 
    content_type_id = ContentType.objects.get_for_model(object).pk,
    object_id       = object.pk,
    object_repr     = force_unicode(object), 
    action_flag     = ADDITION
)

ここで、objectはもちろん変更されたオブジェクトです。

今、私はダニエルの答えを見て、彼に同意します、それはかなり限られています。

私の意見では、より強力なアプローチは、Marty Alchinの本 Pro Django のコードを使用することです(263ページ以降のKeeping Historical Recordsを参照) 。このアプローチを実装および拡張するアプリケーション Django-simple-history があります( docs here )。

125
Van Gale

管理者の変更履歴ログは、Django.contrib.admin.models、およびhistory_view標準のModelAdminクラスのメソッド。

しかし、それらは特に賢くはなく、管理者とかなり密接に結びついているので、アイデアのためにこれらを使用し、アプリ用に独自のバージョンを作成するのが最善かもしれません。

21
Daniel Roseman

この質問は古いことは知っていますが、今日(Django 1.9)の時点では、Djangoの履歴項目はこの質問の日付よりも堅牢です。現在のプロジェクトでは、最近の履歴項目を取得して、ナビゲーションバーからドロップダウンに配置する必要がありました。これは私がそれをやった方法であり、非常に簡単でした:

*views.py*    

from Django.contrib.admin.models import LogEntry, ADDITION, CHANGE, DELETION

def main(request, template):

    logs = LogEntry.objects.exclude(change_message="No fields changed.").order_by('-action_time')[:20]
    logCount = LogEntry.objects.exclude(change_message="No fields changed.").order_by('-action_time')[:20].count()

    return render(request, template, {"logs":logs, "logCount":logCount})

上記のコードスニペットに見られるように、LogEntryモデル(Django.contrib.admin.models.pyはDjango 1.9にある場所)から基本的なクエリセットを作成し、変更が関係しない項目を除外しています、アクション時間で並べ替え、過去20ログのみを表示します。また、カウントだけで別のアイテムを取得しています。 LogEntryモデルを見ると、Djangoが必要なデータを引き戻すために使用したフィールド名を見ることができます。私の場合、テンプレートで使用したものは次のとおりです。

最終製品の画像へのリンク

*template.html*

<ul class="dropdown-menu">
    <li class="external">
        <h3><span class="bold">{{ logCount }}</span> Notification(s) </h3>
        <a href="{% url 'index' %}"> View All </a>
    </li>
        {% if logs %}
            <ul class="dropdown-menu-list scroller actionlist" data-handle-color="#637283" style="height: 250px;">
                {% for log in logs %}
                    <li>
                        <a href="javascript:;">
                            <span class="time">{{ log.action_time|date:"m/d/Y - g:ia" }} </span>
                            <span class="details">
                                {% if log.action_flag == 1 %}
                                    <span class="label label-sm label-icon label-success">
                                        <i class="fa fa-plus"></i>
                                    </span>
                                {% Elif log.action_flag == 2 %}
                                    <span class="label label-sm label-icon label-info">
                                        <i class="fa fa-edit"></i>
                                    </span>
                                {% Elif log.action_flag == 3 %}
                                    <span class="label label-sm label-icon label-danger">
                                        <i class="fa fa-minus"></i>
                                    </span>
                                {% endif %}
                                {{ log.content_type|capfirst }}: {{ log }}
                            </span>
                        </a>
                    </li>
                 {% endfor %}
            </ul>
        {% else %}
            <p>{% trans "This object doesn't have a change history. It probably wasn't added via this admin site." %}</p>
        {% endif %}
    </li>
</ul>
10
dave4jr

すでに言われたことに追加するために、ここにあなたのための他のいくつかのリソースがあります:

(1)私は Django-reversion というアプリで作業してきました。これは管理履歴に「フック」し、実際に追加します。見たくなる良いサンプルコードが必要な場合。

(2)独自の履歴機能をロールすることにした場合Djangoは、各履歴オブジェクトのpost_saveのように、アプリハンドルをサブスクライブできるシグナルを提供します。コードは毎回実行されます履歴ログエントリが保存されましたDoc: Django Signals

7
T. Stone

サンプルコード

こんにちは、

最近、サーバーインベントリデータベースの「更新」ビューへのロギングをハッキングしました。 「サンプル」コードを共有すると思いました。次の関数は、「サーバー」オブジェクトの1つ、変更されたもののリスト、およびADDITIONまたはCHANGEのaction_flagを取ります。 ADDITIONが「新しいサーバーを追加した」ことを意味するところを少し簡素化します。より柔軟なアプローチでは、サーバーに属性を追加できます。もちろん、既存の機能を監査して変更が実際に行われたかどうかを判断することは十分に困難だったので、新しい属性を「変更」として記録することができてうれしいです。

from Django.contrib.admin.models import LogEntry, User, ADDITION, CHANGE
from Django.contrib.contenttypes.models import ContentType

def update_server_admin_log(server, updated_list, action_flag):
    """Log changes to Admin log."""
    if updated_list or action_flag == ADDITION:
        if action_flag == ADDITION:
            change_message = "Added server %s with hostname %s." % (server.serial, server.name)
        # http://dannyman.toldme.com/2010/06/30/python-list-comma-comma-and/
        Elif len(updated_list) > 1:
            change_message = "Changed " + ", ".join(map(str, updated_list[:-1])) + " and " + updated_list[-1] + "."
        else:
            change_message = "Changed " + updated_list[0] + "."
        # http://stackoverflow.com/questions/987669/tying-in-to-Django-admins-model-history
        try:
            LogEntry.objects.log_action(
                # The "update" user added just for this purpose -- you probably want request.user.id
                user_id = User.objects.get(username='update').id,
                content_type_id = ContentType.objects.get_for_model(server).id,
                object_id = server.id,
                # HW serial number of our local "Server" object -- definitely change when adapting ;)
                object_repr = server.serial,
                change_message = change_message,
                action_flag = action_flag,
                )
        except:
            print "Failed to log action."
2
dannyman