Explicit, intent-aware audit trail library for Django and Django REST Framework
Project description
Django System Audit
An explicit, intent-aware audit trail library for Django and Django REST Framework.
Django System Audit helps you reliably answer audit questions such as:
Who did what, when, where, and why — across Django Admin, DRF APIs, and custom application workflows.
It is designed for correctness over convenience, and for systems where audit data must be defensible, not guessed.
Why This Library Exists
Most Django “activity” or “audit” libraries rely heavily on:
- model signals
- implicit inference
- global hooks
- or magical auto-tracking
These approaches break down when you need:
- correct actor attribution
- intent-aware auditing (API vs Admin vs system)
- compliance-grade audit trails
- predictable, testable behavior
Django System Audit is intentionally explicit. It prefers intent over inference, and clarity over magic.
✨ Key Features
-
✅ Single, centralized audit service (
track_activity) -
✅ Correct actor attribution
- user
- system
- anonymous (explicitly allowed)
-
✅ First-class support for:
- Django Admin
- DRF ViewSets
- APIView / GenericAPIView
- JWT / OAuth authentication
-
✅ Intent-level auditing
- Admin & API hooks override signals
- Signals act only as a fallback
-
✅ Field-level diffs (opt-in, PII-safe)
-
✅ Request context capture
- IP address
- User agent
- HTTP method
- Request path
-
✅ Async-safe
- Uses
contextvars, not thread-locals
- Uses
-
✅ No duplicate logs
- Automatic per-request signal suppression
-
✅ Explicit > implicit
- No guessing
- No silent behavior
❌ What This Library Does Not Do (By Design)
- ❌ Automatically track bulk updates (
QuerySet.update,bulk_update) - ❌ Infer intent from raw SQL or database triggers
- ❌ Guess actors inside model signals
- ❌ Log sensitive fields unless explicitly allowed
- ❌ Pretend that metadata alone constitutes an audit event
If a system claims to do these safely and automatically, it is incomplete or unsafe.
Installation
pip install django-system-audit
⚠️ PyPI package name and Python import path differ intentionally.
Basic Setup
1️⃣ Add to INSTALLED_APPS
INSTALLED_APPS = [
...
"activity_tracker",
]
2️⃣ Add Middleware (Required)
This middleware propagates:
- actor context
- request metadata
MIDDLEWARE = [
...
"django.contrib.auth.middleware.AuthenticationMiddleware",
"activity_tracker.middleware.ActivityTrackerMiddleware",
...
]
⚠️ Must be after
AuthenticationMiddleware.
3️⃣ Run migrations
python manage.py makemigrations activity_tracker
python manage.py migrate
Core Concept (Critical to Understand)
Everything funnels through one function:
track_activity(...)
All integrations—Admin, DRF, helpers, signals—eventually call this service.
This guarantees:
- consistent behavior
- predictable metadata
- testable invariants
- safe refactoring over time
Basic Usage
Manual Tracking (Anywhere)
from activity_tracker.services import track_activity
track_activity(
action="CUSTOM_EVENT",
actor=request.user,
metadata={"reason": "manual_override"},
)
Use this for:
- background jobs
- management commands
- custom workflows
- system events
Django Admin Integration (Recommended)
Admin actions represent explicit human intent. They should always be audited directly.
Enable Admin Auditing
from django.contrib import admin
from activity_tracker.admin_mixins import AuditModelAdminMixin
from .models import Article
@admin.register(Article)
class ArticleAdmin(AuditModelAdminMixin, admin.ModelAdmin):
pass
What You Get
- Correct actor (
request.user) - Exact field diffs (
form.changed_data) - CREATE / UPDATE / DELETE events
- Signal suppression (no duplicates)
Django REST Framework (DRF)
✅ Best Option: ViewSets (Recommended)
from rest_framework.viewsets import ModelViewSet
from activity_tracker.drf_mixins import AuditModelViewSetMixin
class ArticleViewSet(AuditModelViewSetMixin, ModelViewSet):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
Guarantees
- Intent-level diffs (
serializer.validated_data) - Explicit actor
- Correct metadata
- No race conditions
- Signals disabled automatically
APIView / GenericAPIView (Explicit Helpers)
For views without lifecycle hooks:
from activity_tracker.helpers import audit_update
class ArticleUpdateAPI(UpdateAPIView):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
def perform_update(self, serializer):
instance = self.get_object()
validated_data = serializer.validated_data
instance = serializer.save()
audit_update(
request=self.request,
instance=instance,
validated_data=validated_data,
)
Available helpers:
audit_createaudit_updateaudit_delete
JWT / OAuth Authentication
Stateless authentication does not trigger Django login signals.
You must log these events explicitly:
from activity_tracker.drf import track_login, track_logout
track_login(request=request, user=user, auth_type="jwt")
track_logout(request=request, user=request.user, auth_type="jwt")
This is intentional and correct.
CRUD Signals (Fallback Only)
Model signals exist as a safety net, not a primary mechanism.
Opt-In via Mixin
from activity_tracker.model_signals import TrackModelActivityMixin
class Article(TrackModelActivityMixin, models.Model):
title = models.CharField(max_length=255)
Signal Behavior
Signals are automatically disabled per request when:
- Admin mixins are used
- DRF ViewSet mixins are used
This guarantees zero duplicate audit entries.
Field-Level Diff Tracking (Advanced)
Diff tracking is explicit, opt-in, and PII-aware.
class Article(TrackModelActivityMixin, models.Model):
title = models.CharField(max_length=255)
body = models.TextField()
TRACK_FIELDS = {"title", "body"}
SENSITIVE_FIELDS = {"body"}
Example diff:
{
"diff": {
"title": {"old": "Old", "new": "New"},
"body": {"old": "***REDACTED***", "new": "***REDACTED***"}
}
}
Metadata Model
Each audit record stores:
actor(user / system / null)action(normalized string)target(GenericForeignKey, optional)metadata(JSON)created_at
Metadata Merge Rules
- Request metadata is added automatically
- Explicit metadata always overrides
- No cross-request leakage
Philosophy (Read This Before Using)
Signals detect effects. Views and Admin express intent. Audit systems must prefer intent over inference.
This library is explicit by design:
- Less magic
- More correctness
- Fewer legal surprises
Common Pitfalls (Avoid These)
- ❌ Expecting signals to capture API intent
- ❌ Expecting bulk updates to be audited
- ❌ Logging raw PII in diffs
- ❌ Relying on thread-locals
- ❌ Treating metadata as an audit event
Compatibility
- Python 3.9+
- Django 3.2+
- Django REST Framework (optional)
License
MIT License
MIT License
Copyright (c) 2024 Rajat Jog
Final Note
This library is built for:
- healthcare systems
- fintech platforms
- internal admin tools
- compliance-sensitive products
If you want magic logging, this is not it. If you want audit data you can defend, you’re in the right place.
🤝 Contributing
Contributions are welcome — but intentional design comes first.
This project prioritizes:
- correctness over convenience
- explicit intent over implicit inference
- predictable, testable behavior over magic
Before contributing, please align with these principles.
How to Contribute
-
Open an issue first
- Describe the problem clearly
- Explain why existing behavior is insufficient
- Propose a solution aligned with the project philosophy
-
Fork and create a feature branch
git checkout -b feature/your-feature-name
-
Add or update tests
- New behavior must be covered
- Bug fixes must include regression tests
-
Keep changes focused
- One concern per PR
- Avoid unrelated refactors
-
Run tests before submitting
pytest
-
Submit a pull request
- Clearly describe intent, not just implementation
- Explain trade-offs if any exist
What Will Likely Be Rejected
To save everyone time, PRs are unlikely to be accepted if they:
- ❌ Add implicit or “magic” behavior
- ❌ Automatically audit bulk updates or raw SQL
- ❌ Guess actors or intent
- ❌ Log sensitive data without explicit opt-in
- ❌ Break existing audit invariants
This library is intentionally conservative.
🧭 Roadmap (High-Level)
Planned or possible future work:
- Audit export utilities (CSV / JSON)
- Retention and archival helpers
- Async offloading (Celery / Kafka)
- Admin-side audit dashboards
- Policy-based audit enforcement
If you want to work on any of these, open an issue first.
📬 Maintainer & Contact
Maintained by Rajat Jog.
- GitHub: https://github.com/
- Email:
rajatjog1294@gmail.com - LinkedIn (optional): https://www.linkedin.com/in/rajat-jog/
For:
- architectural questions
- security concerns
- compliance-related discussions
please prefer direct contact or a private issue where appropriate.
Security Disclosure
If you discover a security or privacy issue:
- Do not open a public issue
- Contact the maintainer directly via email Responsible disclosure is appreciated.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file django_system_audit-0.1.1.tar.gz.
File metadata
- Download URL: django_system_audit-0.1.1.tar.gz
- Upload date:
- Size: 17.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3d1383c918dbc891aeb90f3872d3ddb81a1a5b2c1cb3eff8b05050f0d2c48c9c
|
|
| MD5 |
4ab6c0a5c60ad7a67734a7e57a81a71e
|
|
| BLAKE2b-256 |
def78ea5f6147102bec0bedc0e7add4482019f00f546a86aaf3d3b0113ca0928
|
File details
Details for the file django_system_audit-0.1.1-py3-none-any.whl.
File metadata
- Download URL: django_system_audit-0.1.1-py3-none-any.whl
- Upload date:
- Size: 18.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f1d1133b7609f308fa903a78b91a0e7bed86ef006cba0d75b4e957d15174c5fd
|
|
| MD5 |
bc0218bb0b5ca6ca9d0db1591078ddc2
|
|
| BLAKE2b-256 |
5ee606b851784eb948fbb48d61bc9f462811980a680a5a2a3996c0c7fcc430df
|