An die Modellgeschichte von Django Admin anknüpfen

93

Die Einrichtung:

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

Die Frage:

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

Antworten:

133

Der Admin-Verlauf ist nur eine App wie jede andere Django-App, mit Ausnahme der 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 wie folgt hinzu (schamlos gestohlen von 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
)

Wo objectist das Objekt, das natürlich geändert wurde?

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

Meiner Meinung nach besteht ein stärkerer Ansatz darin, den Code von Marty Alchin in seinem Buch Pro Django zu verwenden (siehe Aufbewahren historischer Aufzeichnungen ab Seite 263). Es gibt eine Anwendung django-simple-history, die diesen Ansatz implementiert und erweitert ( Dokumente hier ).

Van Gale
quelle
7
Vergessen Sie nicht: aus django.contrib.contenttypes.models importieren Sie ContentType. Force_unicode ist auch eine eigene Funktion.
Sakabako
10
from django.utils.encoding import force_unicodefür 'force_unicode'
mmrs151
17
Seit diese Frage beantwortet wurde, wurde Marty Alchins Ansatz in einer Anwendung namens Django-Simple-History als Open-Source-Ansatz erweitert .
Trey Hunner
5
Die neue Heimat der Django-Simple-Geschichte scheint zu sein: github.com/treyhunner/django-simple-history Weitere Informationen zu RTD django-simple-history.readthedocs.org/en/latest
Brutus
3
Ein guter Ansatz kann auch darin bestehen, das Vergleichsraster auf djangopackages.com zu überprüfen, in dem django-simple-history und andere Lösungen (wie CleanerVersion oder django-reversion) verglichen werden.
Maennel
21

Das Änderungsprotokoll des Administrators ist in definiert django.contrib.admin.models, und history_viewdie Standardklasse enthält eine Methode ModelAdmin.

Sie sind jedoch nicht besonders clever und ziemlich eng mit dem Administrator verbunden. Verwenden Sie diese also am besten nur für Ideen und erstellen Sie Ihre eigene Version für Ihre App.

Daniel Roseman
quelle
4
Ist das noch wahr?
Klicken Sie hier
11

Ich weiß, dass diese Frage alt ist, aber bis heute (Django 1.9) sind Djangos Geschichtsgegenstände robuster als zum Zeitpunkt dieser Frage. In einem aktuellen Projekt musste ich die aktuellen Verlaufselemente abrufen und in ein Dropdown-Menü in der Navigationsleiste 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 ist, erstelle ich ein grundlegendes Abfrageset aus dem LogEntry-Modell (django.contrib.admin.models.py befindet sich in django 1.9) und schließe die Elemente aus, bei denen keine Änderungen erforderlich sind, und ordne es nach die Aktionszeit und zeigt nur die letzten 20 Protokolle an. Ich bekomme auch einen anderen Artikel mit nur der Zählung. Wenn Sie sich das LogEntry-Modell ansehen, sehen Sie die Feldnamen, die Django verwendet hat, um die benötigten Daten zurückzuziehen. Für meinen 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>
dave4jr
quelle
7

Um das bereits Gesagte zu ergänzen, sind hier einige andere Ressourcen für Sie:

(1) Ich habe mit einer App namens Django-Reversion gearbeitet, die sich in den Admin-Verlauf einfügt und diesen tatsächlich ergänzt. Wenn Sie einen Beispielcode wünschen, ist dies ein guter Ort, um nachzuschauen.

(2) Wenn Sie sich entschieden haben, Ihre eigene Verlaufsfunktionalität zu erweitern, liefert django Signale, die Sie abonnieren können, damit Ihre App beispielsweise post_save für jedes Verlaufsobjekt verarbeitet. Ihr Code wird jedes Mal ausgeführt, wenn ein Verlaufsprotokolleintrag gespeichert wird. Doc: Django-Signale

T. Stone
quelle
3
Ich würde dringend von einer Django-Umkehrung abraten. Im Konzept ist es eine großartige Idee, aber die Implementierung ist schrecklich. Ich habe das in einer Produktionsstätte benutzt und es war ein Albtraum. Anfangs hat es großartig funktioniert, aber ich habe schließlich herausgefunden, dass die App überhaupt nicht skalierbar ist. Daher wird Ihr Administrator für alle Modelle mit halb häufigen Änderungen in einigen Monaten unbrauchbar, da die verwendeten Abfragen schrecklich ineffizient sind.
Cerin
@Cerin und andere: Ist das noch wahr? Ich versuche zu sehen, ob ich Django-Reversion für eine Site mit vielen Inhalten verwenden kann. django-reversion scheint auf djangopackages.org- und SO-Posts am besten bewertet zu sein, aber die Skalierbarkeit ist eine wichtige Priorität für meine App, daher frage ich
Anupam
1
@Anupam, ich habe es nicht verwendet, da ich es von meiner Produktionsstätte aus deaktivieren musste. Ich habe die Probleme als Fehler gemeldet, aber der Entwickler hat mich umgehauen und gesagt, dass es kein Problem ist, deshalb habe ich das Projekt nicht neu bewertet.
Cerin
Ich verstehe - macht es Ihnen etwas aus, den Problemlink bitte zu teilen? Wird super hilfreich für mich sein, da ich ernsthaft überlege, ob ich es für meine Django App verwenden soll oder nicht
Anupam
3

Beispielcode

Hallo,

Ich habe kürzlich einige Protokolle 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 Dinge und ein action_flag von ADDITION oder CHANGE. Es vereinfacht die Dinge ein wenig, wobei 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 ausreichend schwierig, unsere vorhandenen Funktionen zu überprüfen, um festzustellen, ob tatsächlich Änderungen stattgefunden haben. Daher bin ich froh, 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."
Dannyman
quelle