Add history tracking functionality using django-pghistory; implement views, templates, and middleware for event serialization and context management

This commit is contained in:
pacnpal
2025-02-09 09:52:19 -05:00
parent d3c01c7eb7
commit 52cb51cb14
14 changed files with 210 additions and 7 deletions

View File

@@ -2,6 +2,8 @@ from django.contrib import admin
from django.contrib.admin import AdminSite
from django.utils.html import format_html
from django.urls import reverse
from django.utils.safestring import mark_safe
import pghistory
from .models import EditSubmission, PhotoSubmission
class ModerationAdminSite(AdminSite):
@@ -75,6 +77,33 @@ class PhotoSubmissionAdmin(admin.ModelAdmin):
obj.reject(request.user, obj.notes)
super().save_model(request, obj, form, change)
class HistoryAdmin(admin.ModelAdmin):
"""Admin interface for viewing model history events"""
list_display = ['pgh_label', 'pgh_created_at', 'get_object_link', 'get_context']
list_filter = ['pgh_label', 'pgh_created_at']
readonly_fields = ['pgh_label', 'pgh_obj_id', 'pgh_data', 'pgh_context', 'pgh_created_at']
date_hierarchy = 'pgh_created_at'
def get_object_link(self, obj):
"""Display a link to the related object if possible"""
if obj.pgh_obj and hasattr(obj.pgh_obj, 'get_absolute_url'):
url = obj.pgh_obj.get_absolute_url()
return format_html('<a href="{}">{}</a>', url, str(obj.pgh_obj))
return str(obj.pgh_obj or '')
get_object_link.short_description = 'Object'
def get_context(self, obj):
"""Format the context data nicely"""
if not obj.pgh_context:
return '-'
html = ['<table>']
for key, value in obj.pgh_context.items():
html.append(f'<tr><th>{key}</th><td>{value}</td></tr>')
html.append('</table>')
return mark_safe(''.join(html))
get_context.short_description = 'Context'
# Register with moderation site only
moderation_site.register(EditSubmission, EditSubmissionAdmin)
moderation_site.register(PhotoSubmission, PhotoSubmissionAdmin)
moderation_site.register(pghistory.models.Event, HistoryAdmin)

View File

@@ -9,11 +9,13 @@ from django.core.exceptions import ObjectDoesNotExist, FieldDoesNotExist
from django.contrib.auth.base_user import AbstractBaseUser
from django.contrib.auth.models import AnonymousUser
from django.utils.text import slugify
import pghistory
from core.models import HistoricalModel
UserType = Union[AbstractBaseUser, AnonymousUser]
class EditSubmission(models.Model):
@pghistory.track() # Track all changes by default
class EditSubmission(HistoricalModel):
STATUS_CHOICES = [
("PENDING", "Pending"),
("APPROVED", "Approved"),
@@ -196,8 +198,8 @@ class EditSubmission(models.Model):
self.handled_at = timezone.now()
self.save()
class PhotoSubmission(models.Model):
@pghistory.track() # Track all changes by default
class PhotoSubmission(HistoricalModel):
STATUS_CHOICES = [
("PENDING", "Pending"),
("APPROVED", "Approved"),
@@ -287,7 +289,6 @@ class PhotoSubmission(models.Model):
if user_role in ["MODERATOR", "ADMIN", "SUPERUSER"]:
self.approve(self.user)
def escalate(self, moderator: UserType, notes: str = "") -> None:
"""Escalate the photo submission to admin"""
self.status = "ESCALATED"