Django REST Framework: How to Customize Router Behavior (With Code Examples)

Last updated 4 months ago | 332 views 75     5

Tags:- Python Django DRF

Introduction: Why Customize Routers in Django REST Framework?

In Django REST Framework (DRF), routers automatically generate URL patterns for your API endpoints. That’s great for rapid development—but what if your project requires:

  • Custom URL patterns not covered by default routers?

  • Unique naming conventions for routes?

  • Advanced routing logic or versioning?

That’s when custom router behavior becomes essential.

By customizing DRF routers, you gain full control over your API structure, allowing you to:

  • Cleanly organize complex endpoints

  • Customize route paths and naming

  • Inject versioning or namespaces

  • Add conditional or dynamic routing logic

Let’s explore how to do it step-by-step.


What is a DRF Router?

A router in DRF is a class responsible for automatically generating URLconf entries for ViewSet classes.

Here are some common routers:

Router Description
SimpleRouter Basic router without trailing slashes
DefaultRouter Adds a root API view
Custom Router Your own class inheriting from SimpleRouter or BaseRouter

Step-by-Step: Writing a Custom DRF Router

Let’s break this into simple parts.

✅ Step 1: Create a ViewSet

# views.py
from rest_framework import viewsets
from rest_framework.response import Response

class ProductViewSet(viewsets.ViewSet):
    def list(self, request):
        return Response({"message": "Product list"})

    def retrieve(self, request, pk=None):
        return Response({"message": f"Product {pk}"})

✅ Step 2: Create a Custom Router

To customize router behavior, subclass SimpleRouter or BaseRouter.

# routers.py
from rest_framework.routers import SimpleRouter

class CustomProductRouter(SimpleRouter):
    def get_routes(self, viewset):
        # Call parent to get the default routes
        routes = super().get_routes(viewset)

        # Here you could modify the routes
        for route in routes:
            # Example: change route name to use dashes instead of underscores
            route.name = route.name.replace('_', '-')

        return routes

You can also override other methods like get_urls() or get_default_basename().


✅ Step 3: Register Your ViewSet Using the Custom Router

# urls.py
from django.urls import path, include
from .views import ProductViewSet
from .routers import CustomProductRouter

router = CustomProductRouter()
router.register(r'products', ProductViewSet, basename='product')

urlpatterns = [
    path('api/', include(router.urls)),
]

Functional Example: Custom Router with Dash Case Names

Here’s a working minimal project setup:

serializers.py

from rest_framework import serializers

class DummySerializer(serializers.Serializer):
    message = serializers.CharField()

views.py

from rest_framework import viewsets
from rest_framework.response import Response

class ProductViewSet(viewsets.ViewSet):
    def list(self, request):
        return Response({"message": "Listing products"})

    def retrieve(self, request, pk=None):
        return Response({"message": f"Retrieving product {pk}"})

routers.py

from rest_framework.routers import SimpleRouter

class CustomRouter(SimpleRouter):
    def get_routes(self, viewset):
        routes = super().get_routes(viewset)

        # Customize route names
        for route in routes:
            if route.name:
                route.name = route.name.replace('_', '-')
        return routes

urls.py

from django.urls import path, include
from .views import ProductViewSet
from .routers import CustomRouter

router = CustomRouter()
router.register(r'products', ProductViewSet, basename='product')

urlpatterns = [
    path('api/', include(router.urls)),
]

Result: /api/products/ and /api/products/<pk>/ still work, but route names now follow a dash-case convention instead of snake_case.


Tips & Common Pitfalls

✅ Best Practices

  • Inherit from SimpleRouter if you just want to adjust the routing logic slightly.

  • Use BaseRouter for fully custom behavior (e.g., when you don’t need DRF’s automatic routes at all).

  • Use basename when your ViewSet lacks a queryset.

⚠️ Common Pitfalls

  • Forgetting to override both get_routes() and get_urls() for full customization.

  • Not handling nested routes properly.

  • Breaking default route structure by overriding too much.

  • Overriding get_routes() without calling super() can remove all routes.


DRF Router Class Comparison

Feature/Behavior SimpleRouter DefaultRouter Custom Router
Auto route generation Optional
Root API view Optional
Custom URL naming
Supports nested routing ❌ (use 3rd-party) ✅ (with work)

Summary and Takeaways

Customizing router behavior in Django REST Framework gives you the flexibility and control to:

  • Define clean, predictable, and developer-friendly URLs

  • Support unconventional naming or structure

  • Introduce advanced routing behavior (e.g., API versioning or conditional routes)

If your project has unique URL requirements or needs to support future scalability, custom routers are your friend.


✅ Best Practices Recap

  • ✅ Inherit from SimpleRouter for light customization

  • ✅ Use get_routes() and get_urls() to fine-tune output

  • ✅ Always test route outputs with python manage.py show_urls or DRF’s API root

  • ✅ Prefer convention until customization becomes necessary