Django Custom Versioning Schemes: Tailoring API Control Your Way

Last updated 4 months ago | 374 views 75     5

Tags:- Python Django DRF

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.