use django-audit-log
This commit is contained in:
parent
db1e28607b
commit
7c5a02bac7
6 changed files with 82 additions and 12 deletions
|
@ -55,7 +55,7 @@ INSTALLED_APPS = [
|
|||
"oauth2_provider",
|
||||
"tz_detect",
|
||||
"sass_processor",
|
||||
"simple_history",
|
||||
"auditlog",
|
||||
"markdownx",
|
||||
"polymorphic",
|
||||
"easy_thumbnails",
|
||||
|
@ -85,7 +85,7 @@ MIDDLEWARE = [
|
|||
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
||||
"hijack.middleware.HijackUserMiddleware",
|
||||
"tz_detect.middleware.TimezoneMiddleware",
|
||||
"simple_history.middleware.HistoryRequestMiddleware",
|
||||
"auditlog.middleware.AuditlogMiddleware",
|
||||
"maintenance_mode.middleware.MaintenanceModeMiddleware", # this should be last
|
||||
]
|
||||
|
||||
|
|
|
@ -10,7 +10,8 @@ class CatalogConfig(AppConfig):
|
|||
from catalog import models
|
||||
from catalog import sites
|
||||
from journal import models as journal_models
|
||||
from catalog.models import init_catalog_search_models
|
||||
from catalog.models import init_catalog_search_models, init_catalog_audit_log
|
||||
from catalog import api
|
||||
|
||||
init_catalog_search_models()
|
||||
init_catalog_audit_log()
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
from functools import cached_property
|
||||
from polymorphic.models import PolymorphicModel
|
||||
from django.db import models
|
||||
import logging
|
||||
|
@ -8,7 +9,6 @@ from django.utils import timezone
|
|||
from django.core.files.uploadedfile import SimpleUploadedFile
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.utils.baseconv import base62
|
||||
from simple_history.models import HistoricalRecords
|
||||
import uuid
|
||||
from typing import cast
|
||||
from .utils import DEFAULT_ITEM_COVER, item_cover_path, resource_cover_path
|
||||
|
@ -17,6 +17,8 @@ from django.conf import settings
|
|||
from users.models import User
|
||||
from django.db import connection
|
||||
from ninja import Schema
|
||||
from auditlog.context import disable_auditlog
|
||||
from auditlog.models import LogEntry, AuditlogHistoryField
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
@ -259,7 +261,6 @@ class Item(SoftDeleteMixin, PolymorphicModel):
|
|||
created_time = models.DateTimeField(auto_now_add=True)
|
||||
edited_time = models.DateTimeField(auto_now=True)
|
||||
is_deleted = models.BooleanField(default=False, db_index=True)
|
||||
history = HistoricalRecords()
|
||||
merged_to_item = models.ForeignKey(
|
||||
"Item",
|
||||
null=True,
|
||||
|
@ -279,6 +280,15 @@ class Item(SoftDeleteMixin, PolymorphicModel):
|
|||
]
|
||||
]
|
||||
|
||||
_content_type_ids = []
|
||||
|
||||
@cached_property
|
||||
def history(self):
|
||||
# can't use AuditlogHistoryField bc it will only return history with current content type
|
||||
return LogEntry.objects.filter(
|
||||
object_id=self.id, content_type_id__in=self._content_type_ids
|
||||
)
|
||||
|
||||
def clear(self):
|
||||
self.set_parent_item(None)
|
||||
self.primary_lookup_id_value = None
|
||||
|
@ -348,13 +358,25 @@ class Item(SoftDeleteMixin, PolymorphicModel):
|
|||
if model not in Item.__subclasses__():
|
||||
raise ValueError("invalid model to recast to")
|
||||
ct = ContentType.objects.get_for_model(model)
|
||||
old_ct = self.polymorphic_ctype
|
||||
tbl = self.__class__._meta.db_table
|
||||
obj = model(item_ptr_id=self.pk, polymorphic_ctype=ct)
|
||||
obj.save_base(raw=True)
|
||||
obj.save(update_fields=["polymorphic_ctype"])
|
||||
with connection.cursor() as cursor:
|
||||
cursor.execute(f"DELETE FROM {tbl} WHERE item_ptr_id = %s", [self.pk])
|
||||
return model.objects.get(pk=obj.pk)
|
||||
with disable_auditlog():
|
||||
# disable audit as serialization won't work here
|
||||
obj = model(item_ptr_id=self.pk, polymorphic_ctype=ct)
|
||||
obj.save_base(raw=True)
|
||||
obj.save(update_fields=["polymorphic_ctype"])
|
||||
with connection.cursor() as cursor:
|
||||
cursor.execute(f"DELETE FROM {tbl} WHERE item_ptr_id = %s", [self.pk])
|
||||
obj = model.objects.get(pk=obj.pk)
|
||||
LogEntry.objects.log_create(
|
||||
obj,
|
||||
action=LogEntry.Action.UPDATE,
|
||||
changes={
|
||||
"polymorphic_ctype_id": [old_ct.id, ct.id],
|
||||
"__model__": [old_ct.model, ct.model],
|
||||
},
|
||||
)
|
||||
return obj
|
||||
|
||||
@property
|
||||
def uuid(self):
|
||||
|
|
|
@ -24,6 +24,7 @@ from .search.models import Indexer
|
|||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.conf import settings
|
||||
import logging
|
||||
from auditlog.registry import auditlog
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
@ -86,3 +87,16 @@ def init_catalog_search_models():
|
|||
Indexer.update_model_indexable(Performance)
|
||||
# Indexer.update_model_indexable(PerformanceProduction)
|
||||
# Indexer.update_model_indexable(CatalogCollection)
|
||||
|
||||
|
||||
def init_catalog_audit_log():
|
||||
for cls in Item.__subclasses__():
|
||||
auditlog.register(
|
||||
cls, exclude_fields=["metadata", "created_time", "edited_time"]
|
||||
)
|
||||
|
||||
auditlog.register(
|
||||
ExternalResource, include_fields=["item", "id_type", "id_value", "url"]
|
||||
)
|
||||
|
||||
Item._content_type_ids = list(all_content_types().values())
|
||||
|
|
|
@ -53,6 +53,39 @@
|
|||
onclick="{% if item %}window.location='{{ item.url }}'{% else %}history.go(-1){% endif %}">
|
||||
</div>
|
||||
</form>
|
||||
{% if request.user.is_superuser %}
|
||||
<div>
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead>
|
||||
<tr style="background:#eee;">
|
||||
<th>Field</th>
|
||||
<th style="width:40%;padding-right: 8px;">From</th>
|
||||
<th style="width:40%;">To</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for log in item.history.all %}
|
||||
<tr>
|
||||
<td colspan="3" style="padding-top:1em;">
|
||||
({{ log.id }}) <b>{{ log.actor }} {{ log.get_action_display }} on {{ log.timestamp }}</b>
|
||||
</td>
|
||||
</tr>
|
||||
{% for key, value in log.changes_dict.items %}
|
||||
<tr {% cycle 'style="background:#eee;"' '' %}>
|
||||
<td>{{ key }}</td>
|
||||
<td style="padding-left: 8px">{{ value.0|default:"None" }}</td>
|
||||
<td>{{ value.1|default:"None" }}</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<p>No data.</p>
|
||||
{% endfor %}
|
||||
{% empty %}
|
||||
<p>No history for this item has been logged yet.</p>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<aside class="grid__aside">
|
||||
{% include "_sidebar_edit.html" %}
|
||||
|
|
|
@ -9,7 +9,7 @@ libsass
|
|||
django-compressor
|
||||
django-sass-processor
|
||||
django-rq
|
||||
django-simple-history
|
||||
django-auditlog @ git+https://github.com/jazzband/django-auditlog.git@45591463e8192b4ac0095e259cc4dcea0ac2fd6c
|
||||
django-hijack
|
||||
django-user-messages
|
||||
django-slack
|
||||
|
|
Loading…
Add table
Reference in a new issue