Architecture¶
drf-commons is organized as a structural layer composed atop Django REST Framework’s own extension points. Understanding this architecture is essential for effective integration and extension.
System Layers¶
drf-commons maps to a clean vertical slice of a DRF-based application:
┌─────────────────────────────────────────────┐
│ HTTP Request │
└──────────────────────┬──────────────────────┘
│
┌──────────────────────▼──────────────────────┐
│ Middleware Layer │
│ CurrentUserMiddleware │
│ SQLDebugMiddleware ProfilerMiddleware │
└──────────────────────┬──────────────────────┘
│
┌──────────────────────▼──────────────────────┐
│ View Layer │
│ BaseViewSet BulkViewSet ReadOnlyViewSet │
│ ImportableViewSet BulkImportableViewSet │
│ (CRUD / Bulk / Import / Export Mixins) │
└──────────────────────┬──────────────────────┘
│
┌──────────────────────▼──────────────────────┐
│ Serializer Layer │
│ BaseModelSerializer │
│ BulkUpdateListSerializer │
│ ConfigurableRelatedField variants │
└──────────────────────┬──────────────────────┘
│
┌──────────────────────▼──────────────────────┐
│ Model Layer │
│ BaseModelMixin TimeStampMixin │
│ UserActionMixin SoftDeleteMixin │
│ VersionMixin SlugMixin MetaMixin │
│ IdentityMixin AddressMixin │
└──────────────────────┬──────────────────────┘
│
┌──────────────────────▼──────────────────────┐
│ Service Layer │
│ ExportService FileImportService │
└─────────────────────────────────────────────┘
┌─────────────────────────────────────────────┐
│ Cross-Cutting Concerns │
│ Context User (ContextVar) │
│ StructuredLogger Debug Utilities │
│ Decorators Pagination Filters │
└─────────────────────────────────────────────┘
Package Structure¶
drf_commons/
├── apps.py # Django AppConfig — startup validation
├── __init__.py # Package version export
│
├── common_conf/ # Configuration management
│ ├── settings.py # CommonSettings — COMMON namespace resolution
│ ├── django_settings.py # Test Django configuration
│ └── test_urls.py / test_views.py
│
├── current_user/ # ContextVar-based user management
│ └── utils.py
│
├── models/ # Model mixins and fields
│ ├── base.py # BaseModelMixin + composition mixins
│ ├── mixins.py # JsonModelMixin
│ ├── content.py # SlugMixin, MetaMixin, VersionMixin
│ ├── person.py # IdentityMixin, AddressMixin
│ └── fields.py # CurrentUserField
│
├── views/ # ViewSet classes and action mixins
│ ├── base.py # Pre-composed ViewSet classes
│ └── mixins/
│ ├── crud.py # CRUD action mixins
│ ├── bulk.py # Bulk operation mixins
│ ├── import_export.py # File import/export mixins
│ └── shared.py # Shared mixin utilities
│
├── serializers/ # Serializer and field system
│ ├── base.py # BaseModelSerializer, BulkUpdateListSerializer
│ └── fields/
│ ├── base.py # ConfigurableRelatedField base classes
│ ├── single.py # Single relation field variants
│ ├── many.py # Many-to-many field variants
│ ├── readonly.py # Read-only field variants
│ ├── custom.py # Custom output field
│ └── mixins/ # Field behaviour mixins
│
├── middlewares/ # Django middleware
│ ├── current_user.py # CurrentUserMiddleware (sync/async)
│ └── debug.py # Debug/profiling middleware
│
├── pagination/ # Pagination classes
│ └── base.py
│
├── filters/ # Filter backends
│ └── ordering/
│ └── computed.py # ComputedOrderingFilter
│
├── response/ # Response utilities
│ └── utils.py # success_response, error_response
│
├── decorators/ # Function/method decorators
│ ├── cache.py
│ ├── logging.py
│ ├── database.py
│ └── performance.py
│
├── services/ # Business logic services
│ ├── export_file/ # Multi-format export service
│ └── import_from_file/ # Multi-model import service
│
├── debug/ # Debug and observability
│ ├── logger.py # StructuredLogger
│ └── utils.py # Debug utilities
│
└── utils/ # Internal utilities
└── middleware_checker.py
Design Decisions¶
ContextVar for User Resolution¶
Standard DRF patterns for tracking the current user involve threading request
context through serializer context dictionaries. This pattern is:
Fragile — requires every serializer in the chain to explicitly pass context
Async-unsafe — thread-local storage breaks under concurrent async tasks
Boilerplate-heavy — creates coupling between serializer and view layers
drf-commons uses Python’s contextvars.ContextVar, introduced in
Python 3.7 and designed precisely for this use case. CurrentUserMiddleware
sets the user into the context variable at request start and resets it at
request end. The context is correctly scoped to each coroutine in async
deployments.
# Internal implementation (simplified)
_current_user: ContextVar[Optional[AbstractBaseUser]] = ContextVar(
"_current_user", default=None
)
def get_current_user():
return _current_user.get()
Mixin Composition at Every Layer¶
Every drf-commons component is a mixin, not a base class. This means:
ViewSets are explicit compositions of action mixins
Model classes are explicit compositions of field/behavior mixins
Serializers compose field types from the configurable field system
This pattern makes the behavior of each class fully visible at its definition
site. A developer reading class BulkViewSet immediately sees every action
it provides through its MRO.
Transaction Safety by Default¶
BaseModelSerializer wraps all write
operations in django.db.transaction.atomic(). Bulk operations in
BulkCreateModelMixin and
BulkUpdateModelMixin are also wrapped
in atomic blocks. This ensures partial writes are never committed.
Batch-Size-Aware Bulk Operations¶
Bulk operation mixins read batch size from the COMMON settings namespace
at runtime. This allows operators to tune batch sizes per environment without
code changes. The viewset’s bulk_batch_size attribute overrides the global
setting when set, allowing per-resource tuning.
Lazy Optional Dependency Loading¶
Export and import services use lazy imports:
def export_xlsx(self, data):
try:
import openpyxl
except ImportError:
raise ImproperlyConfigured(
"Install drf-commons[export] to use XLSX export."
)
This keeps the core package installable without any optional dependencies and produces actionable error messages when a feature is used without its dependencies installed.
Startup-Time Validation¶
The DrfCommonsConfig ready() method invokes
enforce_current_user_middleware_if_used().
This function inspects the application’s installed models and raises
django.core.exceptions.ImproperlyConfigured at startup if:
Any model uses
UserActionMixinorCurrentUserFieldCurrentUserMiddlewareis not present inMIDDLEWARE
This surfaces configuration errors at startup rather than at the first API request, a significantly better debugging experience.
Extension Points¶
drf-commons is designed to be extended at every layer:
Component |
Extension Point |
|---|---|
ViewSets |
Compose custom mixins with |
Model Mixins |
Subclass and override |
Serializer Fields |
Subclass |
Response Format |
Override |
Import Config |
Provide |
Export Config |
Provide |
Pagination |
Subclass |
Ordering |
Add |
Logging |
Subclass |
See Extensibility for detailed extension recipes.