webentwicklung-frage-antwort-db.com.de

Anbindung an Django Admin's Model History

Das Setup:

  • Ich arbeite an einer Django - Anwendung, mit der Benutzer ein Objekt in der Datenbank erstellen und es dann so oft bearbeiten können, wie sie möchten.
  • Die Administrations-Site von Django speichert den Verlauf der Änderungen, die an Objekten über die Administrations-Site vorgenommen wurden.

Die Frage:

  • Wie binde ich meine Anwendung in den Änderungsverlauf der Admin-Site ein, damit ich den Verlauf der Änderungen sehen kann, die Benutzer an ihrem "Inhalt" vorgenommen haben?
85
akdom

Der Admin-Verlauf ist nur eine App wie jede andere Django App, mit der Ausnahme einer speziellen Platzierung auf der Admin-Site.

Das Modell befindet sich in Django.contrib.admin.models.LogEntry.

Wenn ein Benutzer eine Änderung vornimmt, fügen Sie diese dem Protokoll hinzu (schamlos aus contrib/admin/options.py gestohlen):

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
)

dabei ist object das Objekt, das natürlich geändert wurde.

Jetzt sehe ich Daniels Antwort und stimme ihm zu, es ist ziemlich begrenzt.

Meiner Meinung nach besteht ein stärkerer Ansatz darin, den Code von Marty Alchin in seinem Buch zu verwenden Pro Django (siehe Historische Aufzeichnungen führen auf Seite 263). Es gibt eine Anwendung Django-simple-history , die diesen Ansatz implementiert und erweitert ( docs here ).

125
Van Gale

Das Änderungsprotokoll des Administrators ist in Django.contrib.admin.models Definiert, und in der Standardklasse ModelAdmin gibt es eine Methode history_view.

Sie sind jedoch nicht besonders clever und ziemlich eng mit dem Administrator verbunden. Verwenden Sie sie daher am besten, um Ideen zu sammeln und eine eigene Version für Ihre App zu erstellen.

21
Daniel Roseman

Ich weiß, dass diese Frage alt ist, aber seit heute (Django 1.9) sind die historischen Elemente von Django robuster als zum Zeitpunkt dieser Frage. In einem aktuellen Projekt musste ich die letzten Verlaufselemente abrufen und sie über die Navigationsleiste in ein Dropdown-Menü einfügen. So habe ich es gemacht und war sehr direkt:

*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})

Wie im obigen Code-Snippet zu sehen, erstelle ich ein grundlegendes Abfrageset aus dem LogEntry-Modell (Django.contrib.admin.models.py befindet sich dort, wo Django 1.9)) die Elemente ausschließt Wenn es sich um keine Änderungen handelt, die nach Aktionszeit geordnet sind und nur die letzten 20 Protokolle anzeigen, wird ein weiteres Element nur mit der Anzahl angezeigt. Wenn Sie sich das LogEntry-Modell ansehen, sehen Sie die Feldnamen, die Django hat verwendet, um die benötigten Daten zurückzugewinnen. In meinem speziellen Fall habe ich Folgendes in meiner Vorlage verwendet:

Link zum Bild des Endprodukts

*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

Im Folgenden finden Sie einige weitere Ressourcen, um das Gesagte zu ergänzen:

(1) Ich habe mit einer App namens Django-reversion gearbeitet, die sich in den Admin-Verlauf einhakt und ihn tatsächlich erweitert. Wenn Sie einen Beispielcode wünschen, ist dies ein guter Ort, um nachzuschauen.

(2) Wenn Sie sich dazu entschlossen haben, Ihre eigene Verlaufsfunktion zu erstellen Django liefert Signale, die Sie abonnieren können, damit Ihre App beispielsweise für jedes Verlaufsobjekt post_save ausführt. Ihr Code wird jedes Mal ausgeführt Ein Eintrag im Verlaufsprotokoll wurde gespeichert. Doc: Django-Signale

7
T. Stone

Beispielcode

Hallo,

Ich habe vor kurzem einige Logs in eine "Update" -Ansicht für unsere Server-Inventardatenbank gehackt. Ich dachte, ich würde meinen "Beispiel" -Code teilen. Die folgende Funktion verwendet eines unserer "Server" -Objekte, eine Liste der geänderten Elemente und ein action_flag von ADDITION oder CHANGE. Es vereinfacht die Dinge ein kleines bisschen, wo ADDITION "einen neuen Server hinzugefügt" bedeutet. Ein flexiblerer Ansatz würde das Hinzufügen eines Attributs zu einem Server ermöglichen. Natürlich war es eine ausreichende Herausforderung, unsere vorhandenen Funktionen zu überprüfen, um festzustellen, ob tatsächlich Änderungen vorgenommen wurden. Daher bin ich glücklich, neue Attribute als "Änderung" zu protokollieren.

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