From 7ecf43f1a4307848d0b6fa75d8a0fe7c2a99f781 Mon Sep 17 00:00:00 2001 From: pacnpal <183241239+pacnpal@users.noreply.github.com> Date: Sun, 9 Feb 2025 09:52:19 -0500 Subject: [PATCH] Add history tracking functionality using django-pghistory; implement views, templates, and middleware for event serialization and context management --- core/middleware.py | 14 +++++++ core/models.py | 17 +++++++- history/apps.py | 12 ++++++ .../history/partials/history_timeline.html | 29 +++++++++++++ history/templatetags/history_tags.py | 17 ++++++++ history/urls.py | 10 +++++ history/views.py | 41 +++++++++++++++++++ moderation/admin.py | 29 +++++++++++++ moderation/models.py | 11 ++--- pyproject.toml | 3 +- requirements.txt | 1 + thrillwiki/settings.py | 4 ++ thrillwiki/urls.py | 1 + uv.lock | 28 +++++++++++++ 14 files changed, 210 insertions(+), 7 deletions(-) create mode 100644 core/middleware.py create mode 100644 history/apps.py create mode 100644 history/templates/history/partials/history_timeline.html create mode 100644 history/templatetags/history_tags.py create mode 100644 history/urls.py create mode 100644 history/views.py diff --git a/core/middleware.py b/core/middleware.py new file mode 100644 index 00000000..c453236b --- /dev/null +++ b/core/middleware.py @@ -0,0 +1,14 @@ +import pghistory + +def setup_pghistory_context(): + """ + Set up pghistory context middleware to track request information. + This function configures what contextual information is stored + with each history record. + """ + pghistory.context(lambda request: { + 'user': str(request.user) if request.user.is_authenticated else None, + 'ip': request.META.get('REMOTE_ADDR'), + 'user_agent': request.META.get('HTTP_USER_AGENT'), + 'session_key': request.session.session_key + }) \ No newline at end of file diff --git a/core/models.py b/core/models.py index 0392ad6a..a717d3e2 100644 --- a/core/models.py +++ b/core/models.py @@ -2,6 +2,18 @@ from django.db import models from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.models import ContentType from django.utils.text import slugify +import pghistory + +@pghistory.track() +class HistoricalModel(models.Model): + """ + Abstract base model that provides universal history tracking via django-pghistory. + """ + class Meta: + abstract = True + + def save(self, *args, **kwargs): + return super().save(*args, **kwargs) class SlugHistory(models.Model): """ @@ -26,9 +38,11 @@ class SlugHistory(models.Model): def __str__(self): return f"Old slug '{self.old_slug}' for {self.content_object}" -class SluggedModel(models.Model): +@pghistory.track() +class SluggedModel(HistoricalModel): """ Abstract base model that provides slug functionality with history tracking. + Inherits from HistoricalModel to get universal history tracking. """ name = models.CharField(max_length=200) slug = models.SlugField(max_length=200, unique=True) @@ -55,6 +69,7 @@ class SluggedModel(models.Model): if not self.slug: self.slug = slugify(self.name) + # Call HistoricalModel's save to ensure history tracking super().save(*args, **kwargs) def get_id_field_name(self): diff --git a/history/apps.py b/history/apps.py new file mode 100644 index 00000000..95bc91d8 --- /dev/null +++ b/history/apps.py @@ -0,0 +1,12 @@ +from django.apps import AppConfig + +class HistoryConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'history' + verbose_name = 'History Tracking' + + def ready(self): + """Initialize app and signal handlers""" + from django.dispatch import Signal + # Create a signal for history updates + self.history_updated = Signal() \ No newline at end of file diff --git a/history/templates/history/partials/history_timeline.html b/history/templates/history/partials/history_timeline.html new file mode 100644 index 00000000..541fcac2 --- /dev/null +++ b/history/templates/history/partials/history_timeline.html @@ -0,0 +1,29 @@ +
{{ event.pgh_data|pprint }}
+ | {key} | {value} |
|---|