Skip to content

Commit 4b33529

Browse files
committed
Configure actions in auditlog.register
1 parent b1ecc8f commit 4b33529

File tree

3 files changed

+59
-34
lines changed

3 files changed

+59
-34
lines changed

auditlog/registry.py

Lines changed: 29 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
)
1616

1717
from auditlog.conf import settings
18+
from auditlog.receivers import log_access, log_create, log_delete, log_update
1819
from auditlog.signals import accessed
1920

2021
DispatchUID = tuple[int, int, int]
@@ -31,34 +32,11 @@ class AuditlogModelRegistry:
3132

3233
DEFAULT_EXCLUDE_MODELS = ("auditlog.LogEntry", "admin.LogEntry")
3334

34-
def __init__(
35-
self,
36-
create: bool = True,
37-
update: bool = True,
38-
delete: bool = True,
39-
access: bool = True,
40-
m2m: bool = True,
41-
custom: Optional[dict[ModelSignal, Callable]] = None,
42-
):
43-
from auditlog.receivers import log_access, log_create, log_delete, log_update
44-
35+
def __init__(self):
4536
self._registry = {}
4637
self._signals = {}
4738
self._m2m_signals = defaultdict(dict)
4839

49-
if create:
50-
self._signals[post_save] = log_create
51-
if update:
52-
self._signals[pre_save] = log_update
53-
if delete:
54-
self._signals[post_delete] = log_delete
55-
if access:
56-
self._signals[accessed] = log_access
57-
self._m2m = m2m
58-
59-
if custom is not None:
60-
self._signals.update(custom)
61-
6240
def register(
6341
self,
6442
model: ModelBase = None,
@@ -70,6 +48,8 @@ def register(
7048
serialize_data: bool = False,
7149
serialize_kwargs: Optional[dict[str, Any]] = None,
7250
serialize_auditlog_fields_only: bool = False,
51+
actions: Optional[dict[str, bool]] = None,
52+
custom: Optional[dict[ModelSignal, Callable]] = None,
7353
):
7454
"""
7555
Register a model with auditlog. Auditlog will then track mutations on this model's instances.
@@ -81,10 +61,31 @@ def register(
8161
:param mask_fields: The fields to mask for sensitive info.
8262
:param m2m_fields: The fields to handle as many to many.
8363
:param serialize_data: Option to include a dictionary of the objects state in the auditlog.
84-
:param serialize_kwargs: Optional kwargs to pass to Django serializer
64+
:param serialize_kwargs: Optional kwargs to pass to Django serializer.
8565
:param serialize_auditlog_fields_only: Only fields being considered in changes will be serialized.
66+
:param actions: Enble log entry on create, update, delete, access and m2m fields.
67+
:param custom: Configure a custom signal when register.
8668
"""
8769

70+
actions = actions or {}
71+
create = actions.get("create", True)
72+
update = actions.get("update", True)
73+
delete = actions.get("delete", True)
74+
access = actions.get("access", True)
75+
m2m = actions.get("m2m", True)
76+
77+
if create:
78+
self._signals[post_save] = log_create
79+
if update:
80+
self._signals[pre_save] = log_update
81+
if delete:
82+
self._signals[post_delete] = log_delete
83+
if access:
84+
self._signals[accessed] = log_access
85+
86+
if custom is not None:
87+
self._signals.update(custom)
88+
8889
if include_fields is None:
8990
include_fields = []
9091
if exclude_fields is None:
@@ -122,7 +123,7 @@ def registrar(cls):
122123
"serialize_kwargs": serialize_kwargs,
123124
"serialize_auditlog_fields_only": serialize_auditlog_fields_only,
124125
}
125-
self._connect_signals(cls)
126+
self._connect_signals(cls, m2m=m2m)
126127

127128
# We need to return the class, as the decorator is basically
128129
# syntactic sugar for:
@@ -180,7 +181,7 @@ def get_serialize_options(self, model: ModelBase):
180181
),
181182
}
182183

183-
def _connect_signals(self, model):
184+
def _connect_signals(self, model, m2m: bool = False):
184185
"""
185186
Connect signals for the model.
186187
"""
@@ -192,7 +193,7 @@ def _connect_signals(self, model):
192193
sender=model,
193194
dispatch_uid=self._dispatch_uid(signal, receiver),
194195
)
195-
if self._m2m:
196+
if m2m:
196197
for field_name in self._registry[model]["m2m_fields"]:
197198
receiver = make_log_m2m_changes(field_name)
198199
self._m2m_signals[model][field_name] = receiver

auditlog_tests/models.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,7 @@
55
from django.db import models
66

77
from auditlog.models import AuditlogHistoryField
8-
from auditlog.registry import AuditlogModelRegistry, auditlog
9-
10-
m2m_only_auditlog = AuditlogModelRegistry(create=False, update=False, delete=False)
8+
from auditlog.registry import auditlog
119

1210

1311
@auditlog.register()
@@ -363,7 +361,15 @@ class AutoManyRelatedModel(models.Model):
363361
auditlog.register(RelatedModel)
364362
auditlog.register(ManyRelatedModel)
365363
auditlog.register(ManyRelatedModel.recursive.through)
366-
m2m_only_auditlog.register(ManyRelatedModel, m2m_fields={"related"})
364+
auditlog.register(
365+
ManyRelatedModel,
366+
m2m_fields=["related"],
367+
actions={
368+
"create": False,
369+
"update": False,
370+
"delete": False,
371+
},
372+
)
367373
auditlog.register(SimpleExcludeModel, exclude_fields=["text"])
368374
auditlog.register(SimpleMappingModel, mapping_fields={"sku": "Product No."})
369375
auditlog.register(AdditionalDataIncludedModel)

auditlog_tests/tests.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
from django.db import models
2020
from django.db.models import JSONField, Value
2121
from django.db.models.functions import Now
22-
from django.db.models.signals import pre_save
22+
from django.db.models.signals import post_delete, post_save, pre_save
2323
from django.test import RequestFactory, TestCase, TransactionTestCase, override_settings
2424
from django.urls import resolve, reverse
2525
from django.utils import dateformat, formats
@@ -34,7 +34,7 @@
3434
from auditlog.middleware import AuditlogMiddleware
3535
from auditlog.models import DEFAULT_OBJECT_REPR, LogEntry
3636
from auditlog.registry import AuditlogModelRegistry, AuditLogRegistrationError, auditlog
37-
from auditlog.signals import post_log, pre_log
37+
from auditlog.signals import accessed, post_log, pre_log
3838
from auditlog_tests.fixtures.custom_get_cid import get_cid as custom_get_cid
3939
from auditlog_tests.models import (
4040
AdditionalDataIncludedModel,
@@ -1438,6 +1438,24 @@ def test_register_from_settings_register_models(self):
14381438
self.assertEqual(fields["include_fields"], ["label"])
14391439
self.assertEqual(fields["exclude_fields"], ["text"])
14401440

1441+
@override_settings(
1442+
AUDITLOG_INCLUDE_TRACKING_MODELS=(
1443+
{
1444+
"model": "auditlog_tests.SimpleModel",
1445+
"actions": {
1446+
"delete": False,
1447+
},
1448+
},
1449+
)
1450+
)
1451+
def test_register_actions_from_settings_models(self):
1452+
self.test_auditlog.register_from_settings()
1453+
1454+
self.assertTrue(self.test_auditlog.contains(SimpleModel))
1455+
self.assertTrue(post_save in self.test_auditlog._signals)
1456+
self.assertTrue(accessed in self.test_auditlog._signals)
1457+
self.assertFalse(post_delete in self.test_auditlog._signals)
1458+
14411459
def test_registration_error_if_bad_serialize_params(self):
14421460
with self.assertRaisesMessage(
14431461
AuditLogRegistrationError,

0 commit comments

Comments
 (0)