mirror of
https://github.com/jazzband/django-auditlog.git
synced 2026-03-16 22:20:26 +00:00
use contextvar instead of threadlocal (#581)
* use contextvar instead of threadlocal * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update auditlog/context.py * update CHANGELOG.md --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Hasan Ramezani <hasan.r67@gmail.com>
This commit is contained in:
parent
a034588ec7
commit
9e987169da
3 changed files with 29 additions and 19 deletions
|
|
@ -3,7 +3,9 @@
|
|||
## Next Release
|
||||
|
||||
#### Improvements
|
||||
|
||||
- Python: Confirm Python 3.12 support ([#572](https://github.com/jazzband/django-auditlog/pull/572))
|
||||
- feat: `thread.local` replaced with `ContextVar` to improve context managers in Django 4.2+
|
||||
|
||||
## 3.0.0-beta.2 (2023-10-05)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,32 +1,33 @@
|
|||
import contextlib
|
||||
import threading
|
||||
import time
|
||||
from contextvars import ContextVar
|
||||
from functools import partial
|
||||
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.db.models.signals import pre_save
|
||||
|
||||
from auditlog.models import LogEntry
|
||||
|
||||
threadlocal = threading.local()
|
||||
auditlog_value = ContextVar("auditlog_value")
|
||||
auditlog_disabled = ContextVar("auditlog_disabled", default=False)
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def set_actor(actor, remote_addr=None):
|
||||
"""Connect a signal receiver with current user attached."""
|
||||
# Initialize thread local storage
|
||||
threadlocal.auditlog = {
|
||||
context_data = {
|
||||
"signal_duid": ("set_actor", time.time()),
|
||||
"remote_addr": remote_addr,
|
||||
}
|
||||
auditlog_value.set(context_data)
|
||||
|
||||
# Connect signal for automatic logging
|
||||
set_actor = partial(
|
||||
_set_actor, user=actor, signal_duid=threadlocal.auditlog["signal_duid"]
|
||||
)
|
||||
set_actor = partial(_set_actor, user=actor, signal_duid=context_data["signal_duid"])
|
||||
pre_save.connect(
|
||||
set_actor,
|
||||
sender=LogEntry,
|
||||
dispatch_uid=threadlocal.auditlog["signal_duid"],
|
||||
dispatch_uid=context_data["signal_duid"],
|
||||
weak=False,
|
||||
)
|
||||
|
||||
|
|
@ -34,12 +35,11 @@ def set_actor(actor, remote_addr=None):
|
|||
yield
|
||||
finally:
|
||||
try:
|
||||
auditlog = threadlocal.auditlog
|
||||
except AttributeError:
|
||||
auditlog = auditlog_value.get()
|
||||
except LookupError:
|
||||
pass
|
||||
else:
|
||||
pre_save.disconnect(sender=LogEntry, dispatch_uid=auditlog["signal_duid"])
|
||||
del threadlocal.auditlog
|
||||
|
||||
|
||||
def _set_actor(user, sender, instance, signal_duid, **kwargs):
|
||||
|
|
@ -48,14 +48,18 @@ def _set_actor(user, sender, instance, signal_duid, **kwargs):
|
|||
This function becomes a valid signal receiver when it is curried with the actor and a dispatch id.
|
||||
"""
|
||||
try:
|
||||
auditlog = threadlocal.auditlog
|
||||
except AttributeError:
|
||||
auditlog = auditlog_value.get()
|
||||
except LookupError:
|
||||
pass
|
||||
else:
|
||||
if signal_duid != auditlog["signal_duid"]:
|
||||
return
|
||||
|
||||
if sender == LogEntry and instance.actor is None:
|
||||
auth_user_model = get_user_model()
|
||||
if (
|
||||
sender == LogEntry
|
||||
and isinstance(user, auth_user_model)
|
||||
and instance.actor is None
|
||||
):
|
||||
instance.actor = user
|
||||
|
||||
instance.remote_addr = auditlog["remote_addr"]
|
||||
|
|
@ -63,11 +67,11 @@ def _set_actor(user, sender, instance, signal_duid, **kwargs):
|
|||
|
||||
@contextlib.contextmanager
|
||||
def disable_auditlog():
|
||||
threadlocal.auditlog_disabled = True
|
||||
token = auditlog_disabled.set(True)
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
try:
|
||||
del threadlocal.auditlog_disabled
|
||||
except AttributeError:
|
||||
auditlog_disabled.reset(token)
|
||||
except LookupError:
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ from functools import wraps
|
|||
|
||||
from django.conf import settings
|
||||
|
||||
from auditlog.context import threadlocal
|
||||
from auditlog.context import auditlog_disabled
|
||||
from auditlog.diff import model_instance_diff
|
||||
from auditlog.models import LogEntry
|
||||
from auditlog.signals import post_log, pre_log
|
||||
|
|
@ -17,7 +17,11 @@ def check_disable(signal_handler):
|
|||
|
||||
@wraps(signal_handler)
|
||||
def wrapper(*args, **kwargs):
|
||||
if not getattr(threadlocal, "auditlog_disabled", False) and not (
|
||||
try:
|
||||
auditlog_disabled_value = auditlog_disabled.get()
|
||||
except LookupError:
|
||||
auditlog_disabled_value = False
|
||||
if not auditlog_disabled_value and not (
|
||||
kwargs.get("raw") and settings.AUDITLOG_DISABLE_ON_RAW_SAVE
|
||||
):
|
||||
signal_handler(*args, **kwargs)
|
||||
|
|
|
|||
Loading…
Reference in a new issue