Django Yöneticisinin Model Geçmişine Bağlanma


96

Kurulum:

  • Kullanıcıların veritabanında bir nesne oluşturmasına ve daha sonra geri dönüp onu istedikleri kadar düzenlemesine izin veren bir Django uygulaması üzerinde çalışıyorum.
  • Django'nun yönetici sitesi, yönetici sitesi aracılığıyla nesnelere yapılan değişikliklerin geçmişini tutar.

Soru:

  • Kullanıcıların "içeriklerinde" yaptıkları değişikliklerin geçmişini görebilmek için uygulamamı yönetici sitesinin değişiklik geçmişine nasıl bağlarım?

Yanıtlar:


138

Yönetici geçmişi, yönetici sitesinde özel yerleşim olması dışında, diğer Django uygulamaları gibi sadece bir uygulamadır.

Model django.contrib.admin.models.LogEntry içindedir.

Bir kullanıcı bir değişiklik yaptığında, günlüğe şu şekilde ekleyin (utanmadan Contrib / admin / options.py'den çalınır:

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
)

objectelbette değiştirilen nesne nerede .

Şimdi Daniel'in cevabını görüyorum ve ona katılıyorum, oldukça sınırlı.

Kanımca daha güçlü bir yaklaşım, Marty Alchin'in Pro Django kitabındaki kodu kullanmaktır (bkz. Geçmiş Kayıtları Saklama sayfa 263'ten başlar). Bir django-simple-history uygulaması varBu yaklaşımı uygulayan ve genişleten uygulaması vardır ( dokümanlar burada ).


9
Unutmayın: django.contrib.contenttypes.models'dan ContentType'ı içe aktarın. Ayrıca force_unicode kendi işlevidir.
sakabako

12
from django.utils.encoding import force_unicode'force_unicode' için
mmrs151

17
Bu soru yanıtlandığından beri Marty Alchin'in yaklaşımı açık kaynaklı ve django-simple-history adlı bir uygulamada genişletildi .
Trey Hunner

5
Django-simple- history'nin yeni evi şöyle görünüyor: github.com/treyhunner/django-simple-history RTD hakkında daha fazla bilgi django-simple-history.readthedocs.org/en/latest
Brutus

3
Django-simple-history ve diğer çözümlerin (CleanerVersion veya django-reversion gibi) karşılaştırıldığı djangopackages.com'daki karşılaştırma tablosunu da kontrol etmek iyi bir yaklaşım olabilir .
maennel

22

Yöneticinin değişiklik geçmişi günlüğü içinde tanımlanmıştır django.contrib.admin.modelsve history_viewstandart ModelAdminsınıfta bir yöntem vardır .

Yine de özellikle akıllı değiller ve yöneticiye oldukça sıkı bir şekilde bağlılar, bu yüzden bunları sadece fikirler için kullanmak ve uygulamanız için kendi sürümünüzü oluşturmak en iyisi olabilir.


4
Bu hala doğru mu?
buraya tıklayın

12

Bu sorunun eski olduğunu biliyorum, ancak bugün itibariyle (Django 1.9), Django'nun geçmiş öğeleri bu sorunun tarihinde olduğundan daha sağlam. Mevcut bir projede, yakın geçmiş öğelerini almam ve onları gezinme çubuğundan bir açılır listeye koymam gerekiyordu. Ben böyle yaptım ve çok açık sözlüydü:

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

Yukarıdaki kod parçacığında görüldüğü gibi, LogEntry modelinden (django.contrib.admin.models.py, django 1.9'da bulunduğu yerdir) temel bir sorgu kümesi oluşturuyorum ve hiçbir değişiklik içermeyen öğeleri hariç tutarak, eylem süresi ve yalnızca son 20 günlüğü gösterir. Ayrıca sadece sayımla başka bir öğe alıyorum. LogEntry modeline bakarsanız, Django'nun ihtiyaç duyduğunuz veri parçalarını geri çekmek için kullandığı alan adlarını görebilirsiniz. Benim özel durumum için, şablonumda kullandım:

Nihai Ürün Görseline Bağlantı

*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

Daha önce söylenenlere eklemek için işte sizin için başka kaynaklar:

(1) django-reversion adlı bir uygulamayla çalışıyorum adlı, yönetici geçmişine 'bağlanan' ve aslında ona eklenen bir uygulamayla çalışıyorum. Örnek bir kod isteseydiniz, burası bakmak için iyi bir yer olurdu.

(2) Kendi geçmiş işlevselliğinizi döndürmeye karar verdiyseniz django, uygulamanızı işlemek için abone olabileceğiniz sinyaller sağlar; örneğin, her geçmiş nesnesi için post_save. Kodunuz, bir geçmiş günlük girişi her kaydedildiğinde çalışır. Doc: Django sinyalleri


3
Ben ediyorum şiddetle django-reversion karşı öneriyoruz. Konsept olarak, bu harika bir fikir, ancak uygulama korkunç. Bunu bir üretim yerinde kullandım ve bu bir kabustu. İlk başta harika çalıştı, ancak sonunda uygulamanın hiç ölçeklenebilir olmadığını öğrendim, bu nedenle yarı sık değişikliklerin olduğu herhangi bir model için, yöneticiniz birkaç ay içinde kullanılamaz hale gelecektir çünkü kullandığı sorgular korkunç derecede verimsizdir.
Cerin

@Cerin ve diğerleri: bu hala doğru mu? Django-reversion'ı çok fazla içeriğe sahip bir site için kullanıp kullanamayacağımı görmeye çalışıyorum. django-reversion, djangopackages.org ve SO gönderilerinde en çok oy alan gibi görünüyor, ancak ölçeklendirebilme uygulamam için önemli bir öncelik, bu yüzden soruyor
Anupam

1
@Anupam, üretim sitemden devre dışı bırakmak zorunda kaldığım için kullanmadım. Sorunları bir hata olarak bildirdim, ancak geliştirici beni havaya uçurdu ve bunun bir sorun olmadığını söyledi, bu yüzden projeyi yeniden değerlendirmedim.
Cerin

Anlıyorum - sorun bağlantısını paylaşır mısınız lütfen? Django uygulamam için kullanıp kullanmamayı ciddi olarak düşündüğüm için benim için çok yardımcı olacak
Anupam

3

Örnek Kod

Merhaba,

Kısa bir süre önce sunucu envanteri veritabanımız için bir "güncelleme" görünümüne giriş yaptım. "Örnek" kodumu paylaşacağımı düşündüm. Aşağıdaki işlev, "Sunucu" nesnelerimizden birini, değiştirilen şeylerin bir listesini ve ADDITION veya CHANGE'ın bir action_flag'ini alır. ADDITION'ın "yeni bir sunucu eklendi" anlamına geldiği durumlarda işleri biraz basitleştirir. Daha esnek bir yaklaşım, bir sunucuya bir öznitelik eklemeye izin verir. Elbette, bir değişikliğin gerçekten gerçekleşip gerçekleşmediğini belirlemek için mevcut işlevlerimizi denetlemek yeterince zordu, bu nedenle yeni öznitelikleri bir "değişiklik" olarak kaydetmekten yeterince mutluyum.

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."

1

Örnek kod:

from django.contrib.contenttypes.models import ContentType  
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=str(object),
    action_flag=ADDITION,
)

Nesne, yönetici site günlüğüne kaydetmek istediğiniz nesnedir.
Object_repr parametresinde str () sınıfıyla deneyebilirsiniz.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.