Views¶
drf-commons provides a complete set of pre-composed ViewSet classes and the action mixins from which they are built. Every ViewSet class is a transparent composition of mixins — no hidden behavior, no magic dispatch.
Pre-Composed ViewSets¶
Class |
Provided Actions |
|---|---|
|
CRUD + file export |
|
CRUD + bulk create/update/delete + file export |
|
List + retrieve + file export |
|
Create + list + file export |
|
Bulk create only |
|
Bulk update only |
|
Bulk delete only |
|
Bulk create + update + delete |
|
CRUD + file import + file export |
|
CRUD + bulk ops + file import + file export |
Import:
from drf_commons.views import (
BaseViewSet,
BulkViewSet,
ReadOnlyViewSet,
CreateListViewSet,
BulkCreateViewSet,
BulkUpdateViewSet,
BulkDeleteViewSet,
BulkOnlyViewSet,
ImportableViewSet,
BulkImportableViewSet,
)
CRUD Mixins¶
CreateModelMixin¶
Handles POST /resource/ for single-object creation.
Configuration:
class MyViewSet(BaseViewSet):
return_data_on_create = True # Default: True
# If False, returns empty data on 201. Reduces response payload.
Response: HTTP 201 with the serialized created object or empty data.
ListModelMixin¶
Handles GET /resource/ for listing objects.
Pagination control:
The list action respects the paginated query parameter:
GET /articles/?paginated=true — paginated results
GET /articles/?paginated=false — all results (use with caution)
GET /articles/ — uses ViewSet default
Index appending:
When append_indexes = True (default on most ViewSets), each result object
receives a sequential index field:
{"id": "...", "title": "...", "index": 1}
This is useful for client-side display ordering.
Configuration:
class MyViewSet(BaseViewSet):
append_indexes = True # Default: True
pagination_class = StandardPageNumberPagination
RetrieveModelMixin¶
Handles GET /resource/{id}/.
Returns standardized success response with the serialized object.
UpdateModelMixin¶
Handles PUT /resource/{id}/ (full update) and PATCH /resource/{id}/
(partial update).
Configuration:
class MyViewSet(BaseViewSet):
return_data_on_update = True # Default: True
# If False, returns empty data on 200.
DestroyModelMixin¶
Handles DELETE /resource/{id}/.
Returns HTTP 204 with standardized success response.
class MyViewSet(BaseViewSet):
def perform_destroy(self, instance):
# Override for soft delete:
instance.soft_delete()
Bulk Operation Mixins¶
BulkCreateModelMixin¶
Provides POST /resource/bulk-create/.
Accepts a JSON array of objects
Validates array format and size against
BULK_OPERATION_BATCH_SIZEWraps in
transaction.atomic()Returns
HTTP 201with created objects (or count ifreturn_data_on_create=False)
# POST /articles/bulk-create/
# Body:
[
{"title": "Article 1", "content": "..."},
{"title": "Article 2", "content": "..."}
]
BulkUpdateModelMixin¶
Provides PUT /resource/bulk-update/ and PATCH /resource/bulk-update/.
PUTvalidates all fields (full update)PATCHuses partial update semanticsAccepts a JSON array with
idfield in each objectInstance count must exactly match incoming data count
Bulk update mode (default, use_save_on_bulk_update = False):
Issues a single bulk_update() SQL statement. Audit fields auto-populated.
Save mode (use_save_on_bulk_update = True):
Calls instance.save() for each object. Triggers signals.
class ProductViewSet(BulkViewSet):
use_save_on_bulk_update = False # default
bulk_batch_size = 500 # override global setting
# PATCH /products/bulk-update/
[
{"id": "uuid-1", "price": "29.99"},
{"id": "uuid-2", "price": "49.99"}
]
BulkDeleteModelMixin¶
Provides:
DELETE /resource/bulk-delete/— Hard deleteDELETE /resource/bulk-soft-delete/— Soft delete (requiresSoftDeleteMixin)
Accepts a JSON array of IDs. Returns a detailed deletion report:
{
"success": true,
"data": {
"requested_count": 10,
"count": 8,
"missing_ids": ["uuid-3", "uuid-7"]
}
}
Import/Export Mixins¶
FileExportMixin¶
Provides POST /resource/export/.
Request body:
{
"file_type": "xlsx",
"includes": ["title", "status", "created_at"],
"column_config": {
"title": {"width": 40, "header": "Article Title"}
},
"data": []
}
Supported file types: csv, xlsx, pdf
Returns an HttpResponse with the file as an attachment.
ViewSet configuration:
class ArticleViewSet(BaseViewSet):
export_field_config = {
"title": "Title",
"published": "Published",
"created_at": "Created At",
}
FileImportMixin¶
Provides POST /resource/import-from-file/.
Request: multipart/form-data with:
file— CSV, XLS, or XLSX fileappend_data—trueto merge with existing,falseto replace
ViewSet configuration:
class EmployeeViewSet(ImportableViewSet):
import_file_config = {
"file_format": "xlsx",
"order": ["department", "employee"],
"models": {
"department": {
"model": Department,
"fields": ["name", "code"],
"unique_fields": ["code"],
},
"employee": {
"model": Employee,
"fields": ["first_name", "last_name", "email"],
"unique_fields": ["email"],
},
},
}
# Optional: transform imported values
import_transforms = {
"email": lambda v: v.strip().lower(),
}
See Services for the full import configuration reference.
Custom ViewSet Composition¶
When no pre-composed ViewSet matches your requirements, compose exactly what you need:
from rest_framework.viewsets import GenericViewSet
from drf_commons.views.mixins import (
ListModelMixin,
BulkCreateModelMixin,
FileExportMixin,
)
class AppendOnlyViewSet(
ListModelMixin,
BulkCreateModelMixin,
FileExportMixin,
GenericViewSet,
):
"""
A resource that allows listing, bulk creation, and export,
but not individual create, update, or delete.
"""
queryset = LogEntry.objects.all()
serializer_class = LogEntrySerializer