Mengikat ke dalam Sejarah Model Admin Django

96

Pengaturan:

  • Saya sedang mengerjakan aplikasi Django yang memperbolehkan pengguna untuk membuat objek dalam database dan kemudian kembali dan mengeditnya sebanyak yang mereka inginkan.
  • Situs admin Django menyimpan riwayat perubahan yang dibuat pada objek melalui situs admin.

Pertanyaan:

  • Bagaimana cara menghubungkan aplikasi saya ke riwayat perubahan situs admin sehingga saya dapat melihat riwayat perubahan yang dibuat pengguna pada "konten" mereka?
akdom
sumber

Jawaban:

137

Sejarah admin hanyalah sebuah aplikasi seperti aplikasi Django lainnya, dengan pengecualian penempatan khusus di situs admin.

Modelnya ada di django.contrib.admin.models.LogEntry.

Ketika pengguna membuat perubahan, tambahkan ke log seperti ini (dicuri tanpa malu-malu dari 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
)

dimana objectobyek yang diubah tentunya.

Sekarang saya melihat jawaban Daniel dan setuju dengannya, itu sangat terbatas.

Menurut pendapat saya pendekatan yang lebih kuat adalah dengan menggunakan kode dari Marty Alchin dalam bukunya Pro Django (lihat Menjaga Catatan Sejarah mulai dari halaman 263). Ada aplikasi django-simple-history yang mengimplementasikan dan memperluas pendekatan ini ( dokumen di sini ).

Van Gale
sumber
9
Jangan lupa: dari django.contrib.contenttypes.models impor ContentType. Selain itu, force_unicode juga merupakan fungsinya sendiri.
sakabako
12
from django.utils.encoding import force_unicodeuntuk 'force_unicode'
mmrs151
17
Sejak pertanyaan ini terjawab, pendekatan Marty Alchin telah menjadi open source dan diperluas dalam aplikasi bernama django-simple-history .
Trey Hunner
5
Rumah baru dari django-simple-history tampaknya adalah: github.com/treyhunner/django-simple-history Info lebih lanjut tentang RTD django-simple-history.readthedocs.org/en/latest
Brutus
3
Pendekatan yang baik juga dapat memeriksa kisi perbandingan di djangopackages.com di mana django-simple-history dan solusi lain (seperti CleanerVersion atau django-reversion) dibandingkan.
maennel
22

Log riwayat perubahan admin ditentukan di django.contrib.admin.models, dan ada history_viewmetode di ModelAdminkelas standar .

Namun, mereka tidak terlalu pintar, dan cukup dekat dengan admin, jadi sebaiknya gunakan saja ini untuk ide dan membuat versi Anda sendiri untuk aplikasi Anda.

Daniel Roseman
sumber
4
Apakah ini masih benar?
klik di sini
12

Saya tahu pertanyaan ini sudah tua, tetapi mulai hari ini (Django 1.9), item sejarah Django lebih kuat daripada pada tanggal pertanyaan ini. Dalam proyek saat ini, saya perlu mendapatkan item sejarah terbaru dan memasukkannya ke dalam menu dropdown dari bilah navigasi. Beginilah cara saya melakukannya dan sangat jujur:

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

Seperti yang terlihat pada potongan kode di atas, saya membuat queryset dasar dari model LogEntry (django.contrib.admin.models.py adalah tempatnya di django 1.9) dan mengecualikan item di mana tidak ada perubahan yang terlibat, memesannya dengan waktu tindakan dan hanya menampilkan 20 log terakhir. Saya juga mendapatkan item lain hanya dengan hitungan. Jika Anda melihat pada model LogEntry, Anda dapat melihat nama bidang yang telah digunakan Django untuk menarik kembali potongan data yang Anda butuhkan. Untuk kasus khusus saya, inilah yang saya gunakan di template saya:

Tautan ke Gambar Produk Akhir

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

Untuk menambah apa yang telah dikatakan, berikut adalah beberapa sumber lain untuk Anda:

(1) Saya telah bekerja dengan aplikasi bernama django-reversion yang 'menghubungkan' ke riwayat admin dan benar-benar menambahkannya. Jika Anda menginginkan beberapa kode contoh yang akan menjadi tempat yang bagus untuk melihat.

(2) Jika Anda memutuskan untuk menggulung fungsionalitas riwayat Anda sendiri, django memberikan sinyal bahwa Anda dapat berlangganan agar aplikasi Anda menangani, misalnya, post_save untuk setiap objek riwayat. Kode Anda akan berjalan setiap kali entri log riwayat disimpan. Doc: Sinyal Django

T. Stone
sumber
3
Saya akan sangat menyarankan agar django-reversion. Secara konsep, ini ide yang bagus, tetapi implementasinya buruk. Saya menggunakan ini di tempat produksi dan itu adalah mimpi buruk. Ini berfungsi dengan baik pada awalnya, tetapi saya akhirnya menemukan bahwa aplikasi sama sekali tidak dapat diskalakan, jadi untuk model apa pun dengan perubahan semi-sering, admin Anda akan menjadi tidak dapat digunakan dalam beberapa bulan karena kueri yang digunakannya sangat tidak efisien.
Cerin
@ Cerin dan lainnya: apakah ini masih benar? Saya mencoba melihat apakah saya dapat menggunakan django-reversion untuk situs dengan banyak konten. django-reversion tampaknya berada di peringkat teratas pada posting djangopackages.org dan SO, tetapi kemampuan untuk menskalakan adalah prioritas penting untuk aplikasi saya, oleh karena itu menanyakan
Anupam
1
@ Anupam, saya belum pernah menggunakannya karena saya harus menonaktifkannya dari situs produksi saya. Saya melaporkan masalah sebagai bug, tetapi pengembang mengejutkan saya dan mengatakan itu bukan masalah, jadi saya belum mengevaluasi ulang proyek tersebut.
Cerin
Saya mengerti - apakah Anda keberatan membagikan tautan masalah? Akan sangat membantu saya karena saya serius mempertimbangkan apakah akan menggunakannya atau tidak untuk aplikasi Django saya
Anupam
3

Kode Contoh

Halo,

Saya baru-baru ini meretas beberapa logging ke tampilan "update" untuk database inventaris server kami. Saya pikir saya akan membagikan kode "contoh" saya. Fungsi berikut ini mengambil salah satu objek "Server" kami, daftar hal-hal yang telah diubah, dan sebuah action_flag dari ADDITION atau CHANGE. Ini menyederhanakan banyak hal di mana ADDITION berarti "menambahkan server baru." Pendekatan yang lebih fleksibel akan memungkinkan untuk menambahkan atribut ke server. Tentu saja, cukup menantang untuk mengaudit fungsi yang ada untuk menentukan apakah perubahan benar-benar telah terjadi, jadi saya cukup senang untuk mencatat atribut baru sebagai "perubahan".

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
sumber