web-dev-qa-db-fra.com

Lier à Django Historique des modèles d'administration

La configuration:

  • Je travaille sur une application Django qui permet aux utilisateurs de créer un objet dans la base de données, puis de revenir en arrière et de le modifier autant qu'ils le souhaitent.
  • Le site d'administration de Django conserve un historique des modifications apportées aux objets via le site d'administration.

La question:

  • Comment connecter mon application à l'historique des modifications du site d'administration afin de voir l'historique des modifications apportées par les utilisateurs à leur "contenu"?
85
akdom

L'historique d'administration est juste une application comme toute autre application Django, à l'exception du placement spécial sur le site d'administration.

Le modèle se trouve dans Django.contrib.admin.models.LogEntry.

Lorsqu'un utilisateur effectue un changement, ajoutez-le au journal comme ceci (volé sans vergogne à 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 est bien sûr l'objet qui a été modifié.

Maintenant je vois la réponse de Daniel et je suis d'accord avec lui, c'est assez limité.

À mon avis, une approche plus forte consiste à utiliser le code de Marty Alchin dans son livre Pro Django (voir Tenir des registres historiques à partir de à la page 263). Il existe une application Django-simple-history qui implémente et étend cette approche ( docs here ).

125
Van Gale

Le journal de l'historique des modifications de l'administrateur est défini dans Django.contrib.admin.models, et il y a un history_view méthode dans la classe standard ModelAdmin.

Cependant, ils ne sont pas particulièrement intelligents et assez étroitement liés à l'administrateur, il est donc préférable de simplement les utiliser pour des idées et de créer votre propre version pour votre application.

21
Daniel Roseman

Je sais que cette question est ancienne, mais à ce jour (Django 1.9), les éléments de l'histoire de Django sont plus robustes qu'ils ne l'étaient à la date de cette question. Dans un projet en cours, j'avais besoin d'obtenir les éléments d'historique récents et de les mettre dans une liste déroulante à partir de la barre de navigation. Voici comment je l'ai fait et j'ai été très simple:

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

Comme vu dans l'extrait de code ci-dessus, je crée un ensemble de requêtes de base à partir du modèle LogEntry (Django.contrib.admin.models.py est l'endroit où il se trouve dans Django 1.9) et en excluant les éléments où aucun changement n'est impliqué, en le classant par temps d'action et en affichant uniquement les 20 derniers journaux. J'obtiens également un autre élément avec juste le nombre. Si vous regardez le modèle LogEntry, vous pouvez voir les noms de champ qui Django a utilisé afin de retirer les données dont vous avez besoin. Pour mon cas spécifique, voici ce que j'ai utilisé dans mon modèle:

Lien vers l'image du produit final

*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

Pour ajouter à ce qui a déjà été dit, voici d'autres ressources pour vous:

(1) J'ai travaillé avec une application appelée Django-reversion qui "se connecte" à l'historique d'administration et y ajoute en fait. Si vous vouliez un exemple de code, ce serait un bon endroit pour regarder.

(2) Si vous décidez de lancer votre propre fonctionnalité d'historique Django fournit des signaux auxquels vous pouvez vous abonner pour que votre application gère, par exemple, post_save pour chaque objet d'historique. Votre code s'exécutera à chaque fois une entrée du journal d'historique a été enregistrée. Doc: signaux Django

7
T. Stone

Exemple de code

Bonjour,

J'ai récemment piraté une connexion à une vue "mise à jour" de notre base de données d'inventaire de serveur. J'ai pensé que je partagerais mon "exemple" de code. La fonction qui suit prend l'un de nos objets "Serveur", une liste des choses qui ont été modifiées et un indicateur action_DITIF ou CHANGE. Cela simplifie un peu les choses où ADDITION signifie "ajouté un nouveau serveur". Une approche plus flexible permettrait d'ajouter un attribut à un serveur. Bien sûr, il était suffisamment difficile de vérifier nos fonctions existantes pour déterminer si des changements avaient effectivement eu lieu, donc je suis assez heureux d'enregistrer de nouveaux attributs en tant que "changement".

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