diff --git a/CHANGES.rst b/CHANGES.rst index 0279c0dc..22031643 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -15,6 +15,7 @@ Unreleased - Added temporary requirement on ``asgiref>=3.6`` while the minimum required Django version is lower than 4.2 (gh-1261) - Small performance optimization of the ``clean-duplicate_history`` command (gh-1015) +- Allow setting change reason through ``SimpleHistoryAdmin``'s change form (gh-1232) 3.4.0 (2023-08-18) ------------------ diff --git a/simple_history/admin.py b/simple_history/admin.py index 34e3033f..f1523097 100644 --- a/simple_history/admin.py +++ b/simple_history/admin.py @@ -1,4 +1,4 @@ -from django import http +from django import forms, http from django.apps import apps as django_apps from django.conf import settings from django.contrib import admin @@ -6,6 +6,7 @@ from django.contrib.admin.utils import unquote from django.contrib.auth import get_permission_codename, get_user_model from django.core.exceptions import PermissionDenied +from django.forms import fields from django.shortcuts import get_object_or_404, render from django.urls import re_path, reverse from django.utils.encoding import force_str @@ -224,9 +225,31 @@ def render_history_view(self, request, template, context, **kwargs): def save_model(self, request, obj, form, change): """Set special model attribute to user for reference after save""" + obj._change_reason = form.cleaned_data["history_change_reason"] obj._history_user = request.user super().save_model(request, obj, form, change) + class form(forms.ModelForm): + history_change_reason = forms.CharField(required=False) + + def get_fields(self, request, obj=None): + return [ + field + for field in super().get_fields(request, obj) + if field != "history_change_reason" + ] + + def get_fieldsets(self, request, obj=None): + return super().get_fieldsets(request, obj) + [ + ( + "History", + { + "classes": ["collapse"], + "fields": ["history_change_reason"], + }, + ) + ] + @property def content_type_model_cls(self): """Returns the ContentType model class.""" diff --git a/simple_history/tests/tests/test_admin.py b/simple_history/tests/tests/test_admin.py index 2b441030..60613890 100644 --- a/simple_history/tests/tests/test_admin.py +++ b/simple_history/tests/tests/test_admin.py @@ -218,6 +218,36 @@ def test_history_user_on_save_in_admin(self): [p.history_user for p in Poll.history.all()], [self.user, self.user] ) + def test_history_change_reason_on_save_in_admin(self): + self.login() + + change_reason = "New change reason" + # Ensure polls created via admin interface save correct change reason + poll_data = { + "question": "new poll?", + "pub_date_0": "2012-01-01", + "pub_date_1": "10:00:00", + "change_reason": change_reason, + } + self.client.post(reverse("admin:tests_poll_add"), data=poll_data) + poll = Poll.objects.get() + self.assertEqual(poll.history.get().history_change_reason, change_reason) + + + change_reason = "Edited change reason" + # Ensure polls modified via admin interface save correct change reason + poll_data = { + "question": "new poll?", + "pub_date_0": "2011-01-01", + "pub_date_1": "10:00:00", + "change_reason": change_reason, + } + self.client.post(reverse("admin:tests_poll_change"), data=poll_data) + poll.refresh_from_db() + self.assertEqual(poll.pub_date_0, "2011-01-01") + self.assertEqual(poll.history.count(), 2) + self.assertEqual(poll.history.latest().history_change_reason, change_reason) + def test_underscore_in_pk(self): self.login() book = Book(isbn="9780147_513731")