Django Namespace Versioning: Organize and Scale Your APIs with Precision
Last updated 4 months ago | 347 views 75 5

Introduction: Why Namespace Versioning Matters
As your Django project evolves, API changes become inevitable—new features get added, response formats are updated, and breaking changes sneak in. Without proper versioning, this can quickly turn into a maintenance nightmare.
Namespace Versioning in Django REST Framework offers a clean, modular approach to managing multiple versions of your API by leveraging Django’s built-in URL namespacing. It allows you to keep separate code paths for different versions while maintaining clean, organized URL routing.
Example:
api/v1/users/
andapi/v2/users/
can point to entirely different viewsets or serializers under the hood.
⚙️ Step-by-Step: Implementing Namespace Versioning
✅ Step 1: Configure DRF to Use NamespaceVersioning
In your settings.py
, set the versioning class to:
REST_FRAMEWORK = {
'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.NamespaceVersioning',
}
⚠️ No need for
DEFAULT_VERSION
orALLOWED_VERSIONS
here.
✅ Step 2: Organize Views per API Version
Create folders or apps for each version. For example:
project/
├── api/
│ ├── v1/
│ │ └── views.py
│ └── v2/
│ └── views.py
✅ Step 3: Define Views
v1/views.py
from rest_framework.response import Response
from rest_framework.views import APIView
class UserViewV1(APIView):
def get(self, request):
return Response({"version": request.version, "users": ["Alice", "Bob"]})
v2/views.py
from rest_framework.response import Response
from rest_framework.views import APIView
class UserViewV2(APIView):
def get(self, request):
return Response({"version": request.version, "users": [{"name": "Alice"}, {"name": "Bob"}]})
✅ Step 4: Set Up Namespaced URLs
In your urls.py
, include versioned apps using Django namespaces.
project/urls.py
from django.urls import path, include
urlpatterns = [
path('api/v1/', include(('api.v1.urls', 'v1'), namespace='v1')),
path('api/v2/', include(('api.v2.urls', 'v2'), namespace='v2')),
]
api/v1/urls.py
from django.urls import path
from .views import UserViewV1
urlpatterns = [
path('users/', UserViewV1.as_view(), name='user-list'),
]
api/v2/urls.py
from django.urls import path
from .views import UserViewV2
urlpatterns = [
path('users/', UserViewV2.as_view(), name='user-list'),
]
Now, DRF detects the version based on the namespace (v1
or v2
) and sets request.version
accordingly.
✅ Functional Example
Calling:
GET /api/v1/users/
Response:
{
"version": "v1",
"users": ["Alice", "Bob"]
}
Calling:
GET /api/v2/users/
Response:
{
"version": "v2",
"users": [
{"name": "Alice"},
{"name": "Bob"}
]
}
Tips & Common Pitfalls
✅ Best Practices
-
Use Django namespaces to cleanly isolate API logic.
-
Create separate apps or folders per version for maintainability.
-
Keep old versions stable and immutable; only patch bugs.
-
Make versioning obvious in documentation and onboarding.
❌ Common Pitfalls and Fixes
Problem | Solution |
---|---|
request.version is None |
Ensure namespace is passed in include() and matches URL |
Using same views for different versions | You can reuse logic but avoid mixing serializers or breaking changes |
Too much duplication | Use shared base classes and override selectively in each version |
Comparison with Other Versioning Schemes
Versioning Type | DRF Class | URL Pattern | Pros | Cons |
---|---|---|---|---|
NamespaceVersioning | NamespaceVersioning |
/api/v1/users/ |
Very clean separation of logic | More setup, duplication risk |
URLPathVersioning | URLPathVersioning |
/api/v1/users/ |
Simple to implement | Less control over routing |
AcceptHeaderVersioning | AcceptHeaderVersioning |
Use HTTP header | Clean URLs, flexible | Harder to debug/test manually |
QueryParameterVersioning | QueryParameterVersioning |
/users/?version=v1 |
Easy for frontend debugging | Less RESTful, messy URLs |
Conclusion: Structure That Scales
Namespace Versioning is ideal for projects with:
-
Strict version requirements
-
Isolated version behavior (views/serializers)
-
Long-term maintenance across API versions
It leverages Django’s native include()
and namespace tools, making it elegant, scalable, and explicit.
Key Takeaways
-
Use NamespaceVersioning when you want a clear, folder-based separation of API versions.
-
Proper namespacing keeps your routing logic organized and maintainable.
-
Always pair versioning with documentation and tests.