Collegamento alla cronologia del modello di Django Admin


94

Il set up:

  • Sto lavorando a un'applicazione Django che consente agli utenti di creare un oggetto nel database e poi tornare indietro e modificarlo quanto desiderano.
  • Il sito di amministrazione di Django conserva una cronologia delle modifiche apportate agli oggetti tramite il sito di amministrazione.

La domanda:

  • Come collego la mia applicazione alla cronologia delle modifiche del sito di amministrazione in modo da poter vedere la cronologia delle modifiche apportate dagli utenti al loro "contenuto"?

Risposte:


136

La cronologia dell'amministratore è solo un'app come qualsiasi altra app Django, con l'eccezione di un posizionamento speciale sul sito di amministrazione.

Il modello è in django.contrib.admin.models.LogEntry.

Quando un utente apporta una modifica, aggiungila al log in questo modo (rubato senza vergogna da 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
)

dov'è objectl'oggetto che è stato cambiato ovviamente.

Ora vedo la risposta di Daniel e sono d'accordo con lui, è piuttosto limitata.

A mio parere un approccio più forte è quello di utilizzare il codice di Marty Alchin nel suo libro Pro Django (vedere Keeping Historical Records a partire da pagina 263). Esiste un'applicazione django-simple-history che implementa ed estende questo approccio ( documenti qui ).


7
Non dimenticare: da django.contrib.contenttypes.models importa ContentType. Inoltre, force_unicode è anche la loro funzione.
Sakabako

10
from django.utils.encoding import force_unicodeper 'force_unicode'
mmrs151

17
Da quando è stata data una risposta a questa domanda, l'approccio di Marty Alchin è stato reso open source ed esteso in un'applicazione chiamata django-simple-history .
Trey Hunner

5
La nuova casa di django-simple-history sembra essere: github.com/treyhunner/django-simple-history Maggiori informazioni su RTD django-simple-history.readthedocs.org/en/latest
Brutus

3
Un buon approccio può anche controllare la griglia di confronto su djangopackages.com dove vengono confrontate django-simple-history e altre soluzioni (come CleanerVersion o django-reversion).
maennel

22

Il registro della cronologia delle modifiche dell'amministratore è definito in django.contrib.admin.modelse c'è un history_viewmetodo nella ModelAdminclasse standard .

Tuttavia, non sono particolarmente intelligenti e sono abbastanza strettamente collegati all'amministratore, quindi potresti essere meglio usarli solo per idee e creare la tua versione per la tua app.


4
È ancora vero?
clicca qui

12

So che questa domanda è vecchia, ma ad oggi (Django 1.9), gli elementi della storia di Django sono più robusti di quanto non fossero alla data di questa domanda. In un progetto in corso, avevo bisogno di ottenere gli elementi della cronologia recente e inserirli in un menu a discesa dalla barra di navigazione. Ecco come l'ho fatto ed è stato molto semplice:

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

Come si vede nello snippet di codice sopra, sto creando un set di query di base dal modello LogEntry (django.contrib.admin.models.py è dove si trova in django 1.9) ed escludendo gli elementi in cui non sono coinvolte modifiche, ordinandolo per il tempo di azione e mostrando solo gli ultimi 20 log. Ricevo anche un altro oggetto con solo il conteggio. Se guardi il modello LogEntry, puoi vedere i nomi dei campi che Django ha utilizzato per recuperare i dati di cui hai bisogno. Per il mio caso specifico, ecco cosa ho usato nel mio modello:

Collegamento all'immagine del prodotto finale

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

7

Per aggiungere a quanto già detto, ecco alcune altre risorse per te:

(1) Ho lavorato con un'app chiamata django-reversion che "si aggancia" alla cronologia dell'amministratore e in realtà si aggiunge ad essa. Se volessi un codice di esempio, sarebbe un buon posto dove guardare.

(2) Se hai deciso di eseguire il rollio della tua funzionalità di cronologia, django fornisce segnali a cui puoi iscriverti per fare in modo che la tua app gestisca, ad esempio, post_save per ogni oggetto della cronologia. Il codice viene eseguito ogni volta che viene salvata una voce del registro della cronologia. Doc: segnali Django


3
Consiglio vivamente contro il django-reversion. In linea di principio, è un'ottima idea, ma l'implementazione è terribile. L'ho usato in un sito di produzione ed è stato un incubo. All'inizio ha funzionato alla grande, ma alla fine ho scoperto che l'app non è affatto scalabile, quindi per tutti i modelli con modifiche semi-frequenti, il tuo amministratore diventerà inutilizzabile in pochi mesi perché le query che utilizza sono orribilmente inefficienti.
Cerin

@Cerin e altri: è ancora vero? Sto cercando di vedere se posso usare django-reversion per un sito con molti contenuti. django-reversion sembra essere il più votato su djangopackages.org e post SO, ma essere in grado di scalare è una priorità importante per la mia app, quindi chiedo
Anupam

1
@ Anupam, non l'ho usato da quando ho dovuto disabilitarlo dal mio sito di produzione. Ho segnalato i problemi come un bug, ma lo sviluppatore mi ha lasciato senza fiato e ha detto che non era un problema, quindi non ho rivalutato il progetto.
Cerin

Capisco: ti dispiace condividere il link del problema, per favore? Sarà di grande aiuto per me dato che sto seriamente valutando se usarlo o meno per la mia app Django
Anupam

3

Codice di esempio

Ciao,

Recentemente ho violato alcuni accessi a una vista di "aggiornamento" per il nostro database di inventario del server. Ho pensato che avrei condiviso il mio codice "esempio". La funzione che segue prende uno dei nostri oggetti "Server", un elenco di cose che sono state modificate e un action_flag di ADDITION o CHANGE. Semplifica un po 'le cose dove ADDITION significa "aggiunto un nuovo server". Un approccio più flessibile consentirebbe di aggiungere un attributo a un server. Naturalmente, è stato sufficientemente impegnativo controllare le nostre funzioni esistenti per determinare se un cambiamento fosse effettivamente avvenuto, quindi sono abbastanza felice di registrare nuovi attributi come un "cambiamento".

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."
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.