mirror of
https://github.com/jazzband/django-constance.git
synced 2026-05-23 22:55:51 +00:00
Added Logentry creation
This commit is contained in:
parent
c908b05740
commit
eae9f10412
3 changed files with 113 additions and 2 deletions
|
|
@ -9,7 +9,10 @@ from django import get_version
|
|||
from django.apps import apps
|
||||
from django.contrib import admin
|
||||
from django.contrib import messages
|
||||
from django.contrib.admin.models import CHANGE
|
||||
from django.contrib.admin.models import LogEntry
|
||||
from django.contrib.admin.options import csrf_protect_m
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.template.response import TemplateResponse
|
||||
|
|
@ -94,7 +97,9 @@ class ConstanceAdmin(admin.ModelAdmin):
|
|||
if request.method == "POST" and request.user.has_perm("constance.change_config"):
|
||||
form = form_cls(data=request.POST, files=request.FILES, initial=initial, request=request)
|
||||
if form.is_valid():
|
||||
form.save()
|
||||
changed_fields = form.save()
|
||||
if changed_fields:
|
||||
self._log_config_change(request, changed_fields)
|
||||
messages.add_message(request, messages.SUCCESS, _("Live settings updated successfully."))
|
||||
return HttpResponseRedirect(".")
|
||||
messages.add_message(request, messages.ERROR, _("Failed to update live settings."))
|
||||
|
|
@ -155,6 +160,24 @@ class ConstanceAdmin(admin.ModelAdmin):
|
|||
request.current_app = self.admin_site.name
|
||||
return TemplateResponse(request, self.change_list_template, context)
|
||||
|
||||
def _log_config_change(self, request, changed_fields):
|
||||
"""
|
||||
Create a Django admin LogEntry recording which config fields were changed.
|
||||
|
||||
Uses the standard Django JSON change_message format so that
|
||||
LogEntry.get_change_message() can interpret it correctly.
|
||||
"""
|
||||
ct = ContentType.objects.get_for_model(self.model)
|
||||
change_message = json.dumps([{"changed": {"fields": changed_fields}}])
|
||||
LogEntry.objects.create(
|
||||
user_id=request.user.pk,
|
||||
content_type_id=ct.pk,
|
||||
object_id="Config",
|
||||
object_repr="Config",
|
||||
action_flag=CHANGE,
|
||||
change_message=change_message,
|
||||
)
|
||||
|
||||
def has_add_permission(self, *args, **kwargs):
|
||||
return False
|
||||
|
||||
|
|
|
|||
|
|
@ -124,10 +124,16 @@ class ConstanceForm(forms.Form):
|
|||
self.initial["version"] = version_hash.hexdigest()
|
||||
|
||||
def save(self):
|
||||
"""
|
||||
Save changed config values to the backend.
|
||||
|
||||
Returns a list of config field names that were actually modified.
|
||||
"""
|
||||
for file_field in self.files:
|
||||
file = self.cleaned_data[file_field]
|
||||
self.cleaned_data[file_field] = default_storage.save(join(settings.FILE_ROOT, file.name), file)
|
||||
|
||||
changed_fields = []
|
||||
for name in settings.CONFIG:
|
||||
current = getattr(config, name)
|
||||
new = self.cleaned_data[name]
|
||||
|
|
@ -140,6 +146,9 @@ class ConstanceForm(forms.Form):
|
|||
|
||||
if current != new:
|
||||
setattr(config, name, new)
|
||||
changed_fields.append(name)
|
||||
|
||||
return changed_fields
|
||||
|
||||
def clean_version(self):
|
||||
value = self.cleaned_data["version"]
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import json
|
||||
from datetime import datetime
|
||||
from unittest import mock
|
||||
|
||||
|
|
@ -28,6 +29,11 @@ class TestAdmin(TestCase):
|
|||
self.normaluser.is_staff = True
|
||||
self.normaluser.save()
|
||||
self.options = admin.site._registry[self.model]
|
||||
# Clear ContentType cache to avoid stale content_type_id references
|
||||
# across tests wrapped in transactions.
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
|
||||
ContentType.objects.clear_cache()
|
||||
|
||||
def test_changelist(self):
|
||||
self.client.login(username="admin", password="nimda")
|
||||
|
|
@ -126,7 +132,7 @@ class TestAdmin(TestCase):
|
|||
},
|
||||
)
|
||||
@mock.patch("constance.settings.IGNORE_ADMIN_VERSION_CHECK", True)
|
||||
@mock.patch("constance.forms.ConstanceForm.save", lambda _: None)
|
||||
@mock.patch("constance.forms.ConstanceForm.save", lambda _: [])
|
||||
@mock.patch("constance.forms.ConstanceForm.is_valid", lambda _: True)
|
||||
def test_submit(self):
|
||||
"""
|
||||
|
|
@ -322,6 +328,79 @@ class TestAdmin(TestCase):
|
|||
# Clean up FIELDS to avoid leaking into other tests
|
||||
FIELDS.pop("language_select", None)
|
||||
|
||||
@mock.patch("constance.settings.CONFIG_FIELDSETS", {"FieldSetOne": ("INT_VALUE", "STRING_VALUE")})
|
||||
@mock.patch(
|
||||
"constance.settings.CONFIG",
|
||||
{
|
||||
"INT_VALUE": (1, "some int"),
|
||||
"STRING_VALUE": ("Hello world", "greetings"),
|
||||
},
|
||||
)
|
||||
@mock.patch("constance.settings.IGNORE_ADMIN_VERSION_CHECK", True)
|
||||
@mock.patch("constance.forms.ConstanceForm.save", lambda _: ["INT_VALUE"])
|
||||
@mock.patch("constance.forms.ConstanceForm.is_valid", lambda _: True)
|
||||
def test_log_entry_created_on_change(self):
|
||||
"""Test that a valid LogEntry is created when config values are changed."""
|
||||
from django.contrib.admin.models import CHANGE
|
||||
from django.contrib.admin.models import LogEntry
|
||||
|
||||
request = self.rf.post(
|
||||
"/admin/constance/config/",
|
||||
data={
|
||||
"INT_VALUE": "42",
|
||||
"STRING_VALUE": "Hello world",
|
||||
"version": "123",
|
||||
},
|
||||
)
|
||||
request.user = self.superuser
|
||||
request._dont_enforce_csrf_checks = True
|
||||
|
||||
with mock.patch("django.contrib.messages.add_message"):
|
||||
response = self.options.changelist_view(request, {})
|
||||
|
||||
self.assertIsInstance(response, HttpResponseRedirect)
|
||||
log_entry = LogEntry.objects.latest("pk")
|
||||
self.assertEqual(log_entry.user, self.superuser)
|
||||
self.assertEqual(log_entry.action_flag, CHANGE)
|
||||
self.assertEqual(log_entry.object_repr, "Config")
|
||||
# Verify change_message uses Django's standard JSON format
|
||||
# so that get_change_message() can render it correctly.
|
||||
self.assertEqual(
|
||||
log_entry.get_change_message(),
|
||||
"Changed INT_VALUE.",
|
||||
)
|
||||
|
||||
@mock.patch("constance.settings.CONFIG_FIELDSETS", {"FieldSetOne": ("INT_VALUE",)})
|
||||
@mock.patch(
|
||||
"constance.settings.CONFIG",
|
||||
{
|
||||
"INT_VALUE": (1, "some int"),
|
||||
},
|
||||
)
|
||||
@mock.patch("constance.settings.IGNORE_ADMIN_VERSION_CHECK", True)
|
||||
@mock.patch("constance.forms.ConstanceForm.save", lambda _: [])
|
||||
@mock.patch("constance.forms.ConstanceForm.is_valid", lambda _: True)
|
||||
def test_no_log_entry_when_no_changes(self):
|
||||
"""Test that no LogEntry is created when the form is saved without any changes."""
|
||||
from django.contrib.admin.models import LogEntry
|
||||
|
||||
initial_count = LogEntry.objects.count()
|
||||
request = self.rf.post(
|
||||
"/admin/constance/config/",
|
||||
data={
|
||||
"INT_VALUE": "1",
|
||||
"version": "123",
|
||||
},
|
||||
)
|
||||
request.user = self.superuser
|
||||
request._dont_enforce_csrf_checks = True
|
||||
|
||||
with mock.patch("django.contrib.messages.add_message"):
|
||||
response = self.options.changelist_view(request, {})
|
||||
|
||||
self.assertIsInstance(response, HttpResponseRedirect)
|
||||
self.assertEqual(LogEntry.objects.count(), initial_count)
|
||||
|
||||
def test_labels(self):
|
||||
self.assertEqual(type(self.model._meta.label), str)
|
||||
self.assertEqual(type(self.model._meta.label_lower), str)
|
||||
|
|
|
|||
Loading…
Reference in a new issue