เชื่อมโยงกับประวัติโมเดลของ Django Admin


96

การตั้งค่า:

  • ฉันกำลังทำงานกับแอปพลิเคชั่น Django ซึ่งอนุญาตให้ผู้ใช้สร้างวัตถุในฐานข้อมูลจากนั้นกลับไปแก้ไขได้มากเท่าที่ต้องการ
  • ไซต์ผู้ดูแลระบบของ Django จะเก็บประวัติของการเปลี่ยนแปลงที่เกิดขึ้นกับออบเจ็กต์ผ่านไซต์ผู้ดูแลระบบ

คำถาม:

  • ฉันจะเชื่อมต่อแอปพลิเคชันของฉันเข้ากับประวัติการเปลี่ยนแปลงของไซต์ผู้ดูแลระบบเพื่อให้สามารถดูประวัติการเปลี่ยนแปลงที่ผู้ใช้ทำกับ "เนื้อหา" ได้อย่างไร

คำตอบ:


137

ประวัติผู้ดูแลระบบเป็นเพียงแอปที่เหมือนกับแอป Django อื่น ๆ โดยมีข้อยกเว้นคือตำแหน่งพิเศษในไซต์ผู้ดูแลระบบ

โมเดลอยู่ใน django.contrib.admin.models.LogEntry

เมื่อผู้ใช้ทำการเปลี่ยนแปลงเพิ่มลงในบันทึกเช่นนี้ (ถูกขโมยโดยไร้ยางอายจากผู้สนับสนุน / 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เป็นวัตถุที่มีการเปลี่ยนแปลงแน่นอน

ตอนนี้ฉันเห็นคำตอบของแดเนียลและเห็นด้วยกับเขามันค่อนข้าง จำกัด

ในความคิดของฉันแนวทางที่ดีกว่าคือการใช้รหัสจาก Marty Alchin ในหนังสือPro Djangoของเขา(ดูการเก็บบันทึกประวัติศาสตร์เริ่มต้นที่หน้า 263) มีแอปพลิเคชั่นdjango-simple-historyที่ใช้และขยายแนวทางนี้ ( เอกสารที่นี่ )


9
อย่าลืม: จาก django.contrib.contenttypes.models นำเข้า ContentType นอกจากนี้ force_unicode ยังเป็นฟังก์ชันของตัวเอง
sakabako

12
from django.utils.encoding import force_unicodeสำหรับ 'force_unicode'
mmrs151

17
เนื่องจากคำถามนี้ได้รับการตอบวิธีมาร์ตี้ Alchin ได้รับการเปิดแหล่งที่มาและขยายในโปรแกรมที่เรียกว่าDjango ง่ายประวัติศาสตร์
Trey Hunner

5
บ้านใหม่ของdjango-simple-historyน่าจะเป็น: github.com/treyhunner/django-simple-historyข้อมูลเพิ่มเติมเกี่ยวกับ RTD django-simple-history.readthedocs.org/en/latest
Brutus

3
แนวทางที่ดีอาจตรวจสอบตารางเปรียบเทียบที่ djangopackages.comซึ่งเปรียบเทียบ django-simple-history และโซลูชันอื่น ๆ (เช่น CleanerVersion หรือ django-reversion)
maennel

22

บันทึกประวัติการเปลี่ยนแปลงของผู้ดูแลระบบถูกกำหนดไว้django.contrib.admin.modelsและมีhistory_viewวิธีการในModelAdminคลาสมาตรฐาน

แม้ว่าพวกเขาจะไม่ฉลาดเป็นพิเศษและค่อนข้างเชื่อมโยงกับผู้ดูแลระบบอย่างแน่นหนาดังนั้นคุณควรใช้สิ่งเหล่านี้เพื่อเป็นแนวคิดและสร้างเวอร์ชันของคุณเองสำหรับแอปของคุณ


4
ยังมีอยู่จริงไหม
คลิกที่นี่

12

ฉันรู้ว่าคำถามนี้เก่า แต่ ณ วันนี้ (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ซึ่ง 'hooks into' ประวัติผู้ดูแลระบบและเพิ่มเข้าไป หากคุณต้องการโค้ดตัวอย่างที่น่าดู

(2) หากคุณตัดสินใจที่จะม้วนฟังก์ชันประวัติของคุณเอง django จะให้สัญญาณว่าคุณสามารถสมัครเพื่อให้แอปของคุณจัดการได้เช่น post_save สำหรับวัตถุประวัติแต่ละรายการ รหัสของคุณจะทำงานทุกครั้งที่บันทึกรายการบันทึกประวัติ เอกสาร: สัญญาณ Django


3
ฉันขอแนะนำอย่างยิ่งกับ django-reversion ในแนวคิดเป็นความคิดที่ดี แต่การนำไปใช้นั้นแย่มาก ฉันใช้สิ่งนี้ในไซต์การผลิตและมันเป็นฝันร้าย มันใช้งานได้ดีในตอนแรก แต่ในที่สุดฉันก็พบว่าแอปนั้นไม่สามารถปรับขนาดได้เลยดังนั้นสำหรับรุ่นใด ๆ ที่มีการเปลี่ยนแปลงกึ่งบ่อยผู้ดูแลระบบของคุณจะใช้งานไม่ได้ในอีกไม่กี่เดือนเนื่องจากการสืบค้นที่ใช้นั้นไม่มีประสิทธิภาพอย่างมาก
Cerin

@ เซอรินและคนอื่น ๆ : ยังเป็นจริงหรือไม่? ฉันกำลังพยายามดูว่าฉันสามารถใช้ django-reversion สำหรับไซต์ที่มีเนื้อหาจำนวนมากได้หรือไม่ django-reversion ดูเหมือนจะได้รับคะแนนสูงสุดใน djangopackages.org และโพสต์ SO แต่ความสามารถในการปรับขนาดเป็นสิ่งสำคัญอันดับแรกสำหรับแอปของฉันดังนั้นจึงขอให้
Anupam

1
@Anupam ฉันไม่ได้ใช้เลยเพราะฉันต้องปิดการใช้งานจากไซต์การผลิตของฉัน ฉันรายงานปัญหาว่าเป็นจุดบกพร่อง แต่ผู้พัฒนาทำให้ฉันผิดหวังและบอกว่ามันไม่ใช่ปัญหาดังนั้นฉันจึงไม่ได้ประเมินโครงการอีกครั้ง
Cerin

ฉันเห็น - คุณช่วยแชร์ลิงก์ปัญหาได้ไหม จะเป็นประโยชน์อย่างยิ่งสำหรับฉันเนื่องจากฉันกำลังพิจารณาอย่างจริงจังว่าจะใช้หรือไม่สำหรับแอป Django ของฉัน
Anupam

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."
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.