Django Custom Versioning Schemes: Tailoring API Control Your Way
Last updated 4 months ago | 374 views 75 5

Introduction: Why Custom Versioning in Django Matters
As APIs evolve, maintaining backward compatibility while rolling out new features is a major concern. Django REST Framework (DRF) provides several built-in versioning schemes like URLPathVersioning
, NamespaceVersioning
, and AcceptHeaderVersioning
. But sometimes, these don't fit your project's needs—maybe you need to extract the version from a custom header or a specific cookie.
That’s where custom versioning schemes shine. You get full control over how your API determines the version, letting you adapt to any client or integration requirement.
⚙️ What Is a Versioning Scheme in DRF?
A versioning scheme in DRF is a class that determines how the version is extracted from an incoming request. The version is then made available via request.version
.
Built-in options:
-
URLPathVersioning
– version from URL path (e.g.,/v1/items/
) -
NamespaceVersioning
– version from namespaced URL conf -
QueryParameterVersioning
– version from query param (?version=v1
) -
AcceptHeaderVersioning
– version from request headers
Sometimes, these are not enough.
Step-by-Step: Creating a Custom Versioning Scheme
Let’s create a custom versioning class that reads the API version from a custom HTTP header, e.g., X-API-VERSION
.
✅ Step 1: Create the Custom Versioning Class
# versioning.py
from rest_framework.versioning import BaseVersioning
from rest_framework.exceptions import NotFound
class CustomHeaderVersioning(BaseVersioning):
def determine_version(self, request, *args, **kwargs):
version = request.headers.get('X-API-VERSION')
# You can define allowed versions
allowed_versions = ['v1', 'v2']
if not version:
raise NotFound(detail="API version is required in 'X-API-VERSION' header.")
if version not in allowed_versions:
raise NotFound(detail=f"API version '{version}' is not supported.")
return version
✅ Step 2: Plug It into DRF Settings
In your settings.py
:
REST_FRAMEWORK = {
'DEFAULT_VERSIONING_CLASS': 'your_app.versioning.CustomHeaderVersioning',
}
Replace your_app
with the actual app name where versioning.py
lives.
✅ Step 3: Use request.version
in Views or Logic
from rest_framework.views import APIView
from rest_framework.response import Response
class VersionedAPIView(APIView):
def get(self, request):
return Response({"version": request.version})
Make a request with:
GET /api/items/
X-API-VERSION: v1
Response:
{
"version": "v1"
}
✅ Full Example: Version-Specific Logic
class ItemListView(APIView):
def get(self, request):
version = request.version
if version == 'v1':
data = ["item1", "item2"] # simple list for v1
elif version == 'v2':
data = [{"name": "item1"}, {"name": "item2"}] # detailed for v2
else:
data = []
return Response({"version": version, "data": data})
Tips & Common Pitfalls
✅ Best Practices
-
Always validate the version in your custom scheme.
-
Document clearly what versioning scheme your API uses.
-
Use custom classes when:
-
You have legacy systems expecting versions in unconventional places.
-
You want to enforce headers for version control.
-
❌ Common Mistakes
Mistake | Why It’s a Problem | Fix |
---|---|---|
Skipping version validation | Accepts bad input | Add explicit checks in determine_version() |
Forgetting to update settings | DRF won’t use the new class | Set DEFAULT_VERSIONING_CLASS correctly |
Relying on headers only | Some clients can’t set custom headers | Consider fallbacks (e.g., query params) |
Comparison Table: Built-in vs Custom
Feature | Built-in DRF Classes | Custom Versioning |
---|---|---|
Easy to implement | ✅ | ❌ (requires code) |
Flexible data sources | ❌ | ✅ (headers, cookies, tokens) |
Strict validation | Limited | ✅ Full control |
Best for large projects | ⚠️ Sometimes | ✅ Always |
✅ Use Cases for Custom Versioning
-
Enterprise integrations using token-based versioning
-
IoT devices sending versions via non-standard headers
-
Mobile clients locked to specific API versions
-
Middleware-based routing where version isn't in URL
Conclusion: When Built-ins Aren’t Enough, Customize
Custom versioning gives you power and precision. If your project has unique versioning needs—be it headers, tokens, or even cookies—writing your own scheme is easy and maintainable.
By using Django’s BaseVersioning
, you’re not reinventing the wheel—you’re extending it.
Key Takeaways
-
Custom versioning is a clean, DRY way to handle complex version requirements.
-
Always validate versions to avoid ambiguity or errors.
-
Use
request.version
to drive logic based on version.