DRF-extensions
DRF-extensions is a collection of custom extensions for Django REST Framework, designed to enhance API functionality and performance. It provides features like response caching, conditional requests, nested routes, and bulk operations. The library is actively maintained, with version 0.8.0 released recently, and it follows a regular release cadence to support newer Django and DRF versions.
Warnings
- breaking Version 0.7.0 of `drf-extensions` dropped support for Django versions below 2.2 and earlier DRF versions (specifically requiring DRF 3.9+ for 0.5.0 and DRF 3.12+ for 0.7.0). Ensure your Django and Django REST Framework versions meet the requirements when upgrading `drf-extensions` to avoid compatibility issues.
- gotcha When using bulk operations (e.g., via `ListUpdateModelMixin`, `ListDestroyModelMixin`), `drf-extensions` applies changes directly to the `QuerySet`. This bypasses standard DRF serializer `save()`/`delete()` methods, viewset lifecycle hooks (`pre_save`, `post_save`, `pre_delete`, `post_delete`), and Django model signals. Any custom logic implemented in these areas will NOT be executed during bulk operations.
- gotcha For safety and to prevent accidental mass changes, `drf-extensions` bulk destroy and bulk update operations explicitly require the `X-BULK-OPERATION` header to be present in the request. If this header is missing, the API will return a `400 BAD REQUEST` error.
- gotcha While `drf-extensions` provides powerful caching decorators like `@cache_response`, the underlying caching mechanism relies on Django's cache framework. The default `LocMemCache` backend in Django is not suitable for production, especially in multi-process or distributed environments, as cache entries are local to each process.
Install
-
pip install drf-extensions
Imports
- cache_response
from rest_framework_extensions.cache.decorators import cache_response
- DetailSerializerMixin
from rest_framework_extensions.mixins import DetailSerializerMixin
- PaginateByMaxMixin
from rest_framework_extensions.mixins import PaginateByMaxMixin
- ListUpdateModelMixin
from rest_framework_extensions.bulk_operations.mixins import ListUpdateModelMixin
- ListDestroyModelMixin
from rest_framework_extensions.bulk_operations.mixins import ListDestroyModelMixin
- NestedRouter
from rest_framework_extensions.routers import NestedRouter
- extensions_api_settings
from rest_framework_extensions.settings import extensions_api_settings
Quickstart
import os
from django.db import models
from rest_framework import views, serializers, status
from rest_framework.response import Response
from rest_framework_extensions.cache.decorators import cache_response
# --- Minimal Django setup for demonstration (not for real project) ---
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')
import django
if not django.apps.apps.ready:
django.setup()
# --- Models (if needed) ---
class City(models.Model):
name = models.CharField(max_length=100)
population = models.IntegerField()
class Meta:
app_label = 'myapp' # Required for minimal setup
def __str__(self):
return self.name
# --- Serializers (if needed) ---
class CitySerializer(serializers.ModelSerializer):
class Meta:
model = City
fields = '__all__'
# --- View demonstrating caching ---
class CityListView(views.APIView):
@cache_response(timeout=60 * 15) # Cache for 15 minutes
def get(self, request, *args, **kwargs):
# In a real app, you'd fetch from DB:
# cities = City.objects.all()
# serializer = CitySerializer(cities, many=True)
# For quickstart, simulate data:
data = [
{"name": "New York", "population": 8000000},
{"name": "Los Angeles", "population": 4000000}
]
return Response(data, status=status.HTTP_200_OK)
# --- Example of running the view (simplified) ---
# In a real Django/DRF project, this would be part of urls.py
# and accessible via an HTTP request.
# For demonstration, we can simulate calling the method:
if __name__ == '__main__':
# This part would typically be handled by Django's URL routing
# and request/response cycle.
# For direct execution, we're just showing the decorator effect conceptualy.
print("Simulating API call with caching...")
view = CityListView()
# Simulate a request object minimally for the decorator
class MockRequest:
method = 'GET'
path = '/cities/'
query_params = {}
# Add other attributes that might be accessed by key constructors if needed
mock_request = MockRequest()
response1 = view.get(mock_request)
print(f"First call: {response1.data}")
# A second call within the cache timeout period would ideally return cached data
# (though simulating the actual cache hit without a full Django setup is complex).
# The key takeaway is the decorator's placement.
response2 = view.get(mock_request)
print(f"Second call: {response2.data}")