Skip to content

Commit ad5257f

Browse files
committed
Initial
1 parent 6414b7a commit ad5257f

File tree

16 files changed

+118
-33
lines changed

16 files changed

+118
-33
lines changed

auditlog/__init__.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,32 @@
1+
from __future__ import annotations
2+
13
from importlib.metadata import version
4+
from typing import TYPE_CHECKING
5+
6+
from django.apps import apps as django_apps
7+
from django.conf import settings
8+
from django.core.exceptions import ImproperlyConfigured
9+
10+
if TYPE_CHECKING:
11+
from auditlog.models import AbstractLogEntry
212

313
__version__ = version("django-auditlog")
14+
15+
16+
def get_logentry_model() -> type[AbstractLogEntry]:
17+
"""
18+
Return the LogEntry model that is active in this project.
19+
"""
20+
try:
21+
return django_apps.get_model(
22+
settings.AUDITLOG_LOGENTRY_MODEL, require_ready=False
23+
)
24+
except ValueError:
25+
raise ImproperlyConfigured(
26+
"AUDITLOG_LOGENTRY_MODEL must be of the form 'app_label.model_name'"
27+
)
28+
except LookupError:
29+
raise ImproperlyConfigured(
30+
"AUDITLOG_LOGENTRY_MODEL refers to model '%s' that has not been installed"
31+
% settings.AUDITLOG_LOGENTRY_MODEL
32+
)

auditlog/admin.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@
44
from django.contrib.auth import get_user_model
55
from django.utils.translation import gettext_lazy as _
66

7+
from auditlog import get_logentry_model
78
from auditlog.filters import CIDFilter, ResourceTypeFilter
89
from auditlog.mixins import LogEntryAdminMixin
9-
from auditlog.models import LogEntry
10+
11+
LogEntry = get_logentry_model()
1012

1113

1214
@admin.register(LogEntry)

auditlog/conf.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,8 @@
5555
settings.AUDITLOG_CHANGE_DISPLAY_TRUNCATE_LENGTH = getattr(
5656
settings, "AUDITLOG_CHANGE_DISPLAY_TRUNCATE_LENGTH", 140
5757
)
58+
59+
# Swap default model
60+
settings.AUDITLOG_LOGENTRY_MODEL = getattr(
61+
settings, "AUDITLOG_LOGENTRY_MODEL", "auditlog.LogEntry"
62+
)

auditlog/context.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,15 @@
66
from django.contrib.auth import get_user_model
77
from django.db.models.signals import pre_save
88

9-
from auditlog.models import LogEntry
9+
from auditlog import get_logentry_model
1010

1111
auditlog_value = ContextVar("auditlog_value")
1212
auditlog_disabled = ContextVar("auditlog_disabled", default=False)
1313

1414

15+
LogEntry = get_logentry_model()
16+
17+
1518
@contextlib.contextmanager
1619
def set_actor(actor, remote_addr=None, remote_port=None):
1720
"""Connect a signal receiver with current user attached."""

auditlog/diff.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ def track_field(field):
2020
:return: Whether the given field should be tracked.
2121
:rtype: bool
2222
"""
23-
from auditlog.models import LogEntry
23+
from auditlog import get_logentry_model
24+
25+
LogEntry = get_logentry_model()
2426

2527
# Do not track many to many relations
2628
if field.many_to_many:

auditlog/management/commands/auditlogflush.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
from django.core.management.base import BaseCommand
44
from django.db import connection
55

6-
from auditlog.models import LogEntry
6+
from auditlog import get_logentry_model
7+
8+
LogEntry = get_logentry_model()
79

810

911
class Command(BaseCommand):

auditlog/management/commands/auditlogmigratejson.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
from django.core.management import CommandError, CommandParser
55
from django.core.management.base import BaseCommand
66

7-
from auditlog.models import LogEntry
7+
from auditlog import get_logentry_model
8+
9+
LogEntry = get_logentry_model()
810

911

1012
class Command(BaseCommand):

auditlog/migrations/0001_initial.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ class Migration(migrations.Migration):
6767
),
6868
],
6969
options={
70+
"swappable": "AUDITLOG_LOGENTRY_MODEL",
7071
"ordering": ["-timestamp"],
7172
"get_latest_by": "timestamp",
7273
"verbose_name": "log entry",

auditlog/mixins.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,14 @@
1010
from django.utils.timezone import is_aware, localtime
1111
from django.utils.translation import gettext_lazy as _
1212

13-
from auditlog.models import LogEntry
13+
from auditlog import get_logentry_model
1414
from auditlog.registry import auditlog
1515
from auditlog.signals import accessed
1616

1717
MAX = 75
1818

19+
LogEntry = get_logentry_model()
20+
1921

2022
class LogEntryAdminMixin:
2123
request: HttpRequest

auditlog/models.py

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from django.utils.encoding import smart_str
2424
from django.utils.translation import gettext_lazy as _
2525

26+
from auditlog import get_logentry_model
2627
from auditlog.diff import mask_str
2728

2829
DEFAULT_OBJECT_REPR = "<error forming object repr>"
@@ -107,7 +108,7 @@ def log_m2m_changes(
107108
except ObjectDoesNotExist:
108109
object_repr = DEFAULT_OBJECT_REPR
109110
kwargs.setdefault("object_repr", object_repr)
110-
kwargs.setdefault("action", LogEntry.Action.UPDATE)
111+
kwargs.setdefault("action", get_logentry_model().Action.UPDATE)
111112

112113
if isinstance(pk, int):
113114
kwargs.setdefault("object_id", pk)
@@ -302,17 +303,7 @@ def _mask_serialized_fields(
302303
return data
303304

304305

305-
class LogEntry(models.Model):
306-
"""
307-
Represents an entry in the audit log. The content type is saved along with the textual and numeric
308-
(if available) primary key, as well as the textual representation of the object when it was saved.
309-
It holds the action performed and the fields that were changed in the transaction.
310-
311-
If AuditlogMiddleware is used, the actor will be set automatically. Keep in mind that
312-
editing / re-saving LogEntry instances may set the actor to a wrong value - editing LogEntry
313-
instances is not recommended (and it should not be necessary).
314-
"""
315-
306+
class AbstractLogEntry(models.Model):
316307
class Action:
317308
"""
318309
The actions that Auditlog distinguishes: creating, updating and deleting objects. Viewing objects
@@ -391,6 +382,7 @@ class Action:
391382
objects = LogEntryManager()
392383

393384
class Meta:
385+
abstract = True
394386
get_latest_by = "timestamp"
395387
ordering = ["-timestamp"]
396388
verbose_name = _("log entry")
@@ -550,6 +542,21 @@ def _get_changes_display_for_fk_field(
550542
return f"Deleted '{field.related_model.__name__}' ({value})"
551543

552544

545+
class LogEntry(AbstractLogEntry):
546+
"""
547+
Represents an entry in the audit log. The content type is saved along with the textual and numeric
548+
(if available) primary key, as well as the textual representation of the object when it was saved.
549+
It holds the action performed and the fields that were changed in the transaction.
550+
551+
If AuditlogMiddleware is used, the actor will be set automatically. Keep in mind that
552+
editing / re-saving LogEntry instances may set the actor to a wrong value - editing LogEntry
553+
instances is not recommended (and it should not be necessary).
554+
"""
555+
556+
class Meta(AbstractLogEntry.Meta):
557+
swappable = "AUDITLOG_LOGENTRY_MODEL"
558+
559+
553560
class AuditlogHistoryField(GenericRelation):
554561
"""
555562
A subclass of py:class:`django.contrib.contenttypes.fields.GenericRelation` that sets some default
@@ -570,7 +577,7 @@ class AuditlogHistoryField(GenericRelation):
570577
"""
571578

572579
def __init__(self, pk_indexable=True, delete_related=False, **kwargs):
573-
kwargs["to"] = LogEntry
580+
kwargs["to"] = get_logentry_model()
574581

575582
if pk_indexable:
576583
kwargs["object_id_field"] = "object_id"

0 commit comments

Comments
 (0)