Прив’язка до історії моделей адміністратора Django


93

Налаштування:

  • Я працюю над додатком Django, який дозволяє користувачам створювати об'єкт у базі даних, а потім повертатися та редагувати його скільки завгодно.
  • Сайт адміністратора Django зберігає історію змін, внесених до об’єктів через сайт адміністратора.

Питання:

  • Як підключити свою програму до історії змін сайту адміністратора, щоб я міг бачити історію змін, які користувачі вносять до свого "вмісту"?

Відповіді:


135

Історія адміністратора - це лише програма, як будь-яка інша програма 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об’єкт, який, звичайно, змінили.

Тепер я бачу відповідь Даніеля і погоджуюсь з ним, вона досить обмежена.

На мою думку, більш сильним підходом є використання коду Марті Альхіна у його книзі Pro Django (див. Ведення історичних записів, починаючи зі сторінки 263). Існує додаток django-simple-history, який реалізує та розширює цей підхід ( документи тут ).


7
Не забувайте: з django.contrib.contenttypes.models імпортуйте ContentType. Крім того, force_unicode - це також їх власна функція.
sakabako

10
from django.utils.encoding import force_unicodeдля 'force_unicode'
mmrs151

17
Оскільки на це запитання було дано відповідь, підхід Марті Алхіна був відкритий та розширений у програмі під назвою django-simple-history .
Trey Hunner

5
Здається, новим будинком django-simple-history є: github.com/treyhunner/django-simple-history Докладніше про RTD django-simple-history.readthedocs.org/en/latest
Брут

3
Хороший підхід може також перевірити сітку порівняння на djangopackages.com, де порівнюється django-simple-history та інші рішення (наприклад, CleanerVersion або django-reversion).
манел

22

Журнал історії змін адміністратора визначений у django.contrib.admin.models, а history_viewметод є у стандартному ModelAdminкласі.

Вони не особливо розумні і досить тісно пов’язані з адміністратором, тому вам може бути найкраще просто використовувати їх для ідей та створити власну версію для вашого додатка.


4
Це все ще правда?
натисніть тут

11

Я знаю, що це питання давнє, але станом на сьогодні (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>

7

Щоб додати до вже сказаного, ось деякі інші ресурси для вас:

(1) Я працюю з додатком під назвою django-reversion, який "підключається" до історії адміністратора і фактично додає її. Якщо ви хочете якийсь зразок коду, це було б гарне місце для пошуку.

(2) Якщо ви вирішили прокрутити власну функціональність історії, django подає сигнали, на які ви можете підписатися, щоб мати дескриптор вашого додатка, наприклад, post_save для кожного об’єкта історії. Ваш код запускатиметься кожного разу, коли буде збережено запис журналу історії. Док: сигнали Django


3
Я настійно рекомендую проти django-реверсії. По суті, це чудова ідея, але реалізація жахлива. Я використовував це на виробництві, і це був кошмар. Спочатку це працювало чудово, але врешті-решт я з’ясував, що додаток зовсім не масштабований, тому для будь-яких моделей із напівчастими змінами адміністратор стане непридатним через кілька місяців, оскільки запити, які він використовує, жахливо неефективні.
Cerin

@Cerin та інші: чи це все ще правда? Я намагаюся зрозуміти, чи можу я використовувати django-реверсію для сайту з великою кількістю вмісту. django-reversion, здається, має найвищий рейтинг на публікаціях djangopackages.org та SO, але можливість масштабування є важливим пріоритетом для мого додатка, отже, запитує
Анупам,

1
@Anupam, я не використовував його, оскільки мені довелося вимкнути його зі свого виробничого сайту. Я повідомив про проблеми як про помилку, але розробник підірвав мене і сказав, що це не проблема, тому я не переоцінював проект.
Черін

Бачу - чи не заперечуєте ви, поділившись посиланням на проблему, будь ласка? Буде дуже корисно для мене, оскільки я серйозно роздумую, використовувати його чи ні для мого додатка Django
Анупам,

3

Приклад коду

Здравствуйте,

Нещодавно я зламав деякі журнали для перегляду "оновлення" для нашої бази даних інвентаризації сервера. Я вирішив, що поділюсь своїм "прикладом" коду. Функція, яка йде далі, бере один із наших "Серверних" об'єктів, список речей, які були змінені, та action_flag або ADDITION, або CHANGE. Це спрощує речі, де 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."
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.