Django REST Framework Routers

Automatic URL routing for ViewSets

🔗 What are Routers?

Routers automatically generate URL patterns for your ViewSets. Instead of manually defining URLs for each action, routers create them automatically, ensuring consistent and RESTful URL structures across your API.


# Automatic URL generation
from rest_framework.routers import DefaultRouter

router = DefaultRouter()
router.register(r'books', BookViewSet)
urlpatterns = router.urls
                                    

Generated URLs:

/books/           - List/Create
/books/{id}/      - Retrieve/Update/Delete

Router Types

🎯

SimpleRouter

Basic URL routing

from rest_framework.routers import SimpleRouter

router = SimpleRouter()
router.register(r'books', BookViewSet)
🚀

DefaultRouter

Includes API root view

from rest_framework.routers import DefaultRouter

router = DefaultRouter()
router.register(r'books', BookViewSet)
🔧

Custom Routes

Add custom URL patterns

router.register(r'books', BookViewSet)
urlpatterns = [
    path('custom/', custom_view),
] + router.urls
📋

Multiple Routers

Organize API versions

router_v1 = DefaultRouter()
router_v2 = DefaultRouter()
router_v1.register(r'books', BookViewSetV1)

🔹 DefaultRouter

The most commonly used router that creates a browsable API root view and standard RESTful URL patterns for your ViewSets:

# urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import BookViewSet, AuthorViewSet

# Create router instance
router = DefaultRouter()

# Register ViewSets
router.register(r'books', BookViewSet, basename='book')
router.register(r'authors', AuthorViewSet, basename='author')

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

Generated URL Patterns:

GET    /api/                  - API root view
GET    /api/books/            - List books
POST   /api/books/            - Create book
GET    /api/books/{id}/       - Retrieve book
PUT    /api/books/{id}/       - Update book
PATCH  /api/books/{id}/       - Partial update
DELETE /api/books/{id}/       - Delete book
GET    /api/authors/          - List authors
POST   /api/authors/          - Create author
GET    /api/authors/{id}/     - Retrieve author

🔹 SimpleRouter

A lightweight router without the API root view. Use when you don't need the browsable API homepage:

# urls.py
from django.urls import path, include
from rest_framework.routers import SimpleRouter
from .views import BookViewSet

router = SimpleRouter()
router.register(r'books', BookViewSet, basename='book')

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

Differences from DefaultRouter:

  • No API root view at /api/
  • Slightly smaller URL configuration
  • Same ViewSet URL patterns
  • Better for production APIs without browsable interface

🔹 Registering ViewSets

Register multiple ViewSets to create a complete API structure with organized endpoints:

# urls.py
from rest_framework.routers import DefaultRouter
from .views import (
    BookViewSet,
    AuthorViewSet,
    CategoryViewSet,
    ReviewViewSet
)

router = DefaultRouter()

# Register all ViewSets
router.register(r'books', BookViewSet, basename='book')
router.register(r'authors', AuthorViewSet, basename='author')
router.register(r'categories', CategoryViewSet, basename='category')
router.register(r'reviews', ReviewViewSet, basename='review')

# Use in urlpatterns
urlpatterns = [
    path('api/v1/', include(router.urls)),
]

Complete API Structure:

/api/v1/                - API root
/api/v1/books/          - Books endpoint
/api/v1/authors/        - Authors endpoint
/api/v1/categories/     - Categories endpoint
/api/v1/reviews/        - Reviews endpoint

🔹 Custom Actions with Routers

Routers automatically generate URLs for custom actions defined with the @action decorator:

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

class BookViewSet(viewsets.ModelViewSet):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    
    @action(detail=False, methods=['get'])
    def recent(self, request):
        recent_books = Book.objects.order_by('-created_at')[:10]
        serializer = self.get_serializer(recent_books, many=True)
        return Response(serializer.data)
    
    @action(detail=True, methods=['post'])
    def publish(self, request, pk=None):
        book = self.get_object()
        book.is_published = True
        book.save()
        return Response({'status': 'published'})
# urls.py
router = DefaultRouter()
router.register(r'books', BookViewSet, basename='book')
urlpatterns = router.urls

Generated Custom URLs:

GET  /books/recent/        - Recent books (list action)
POST /books/{id}/publish/  - Publish book (detail action)

🔹 Combining Routers with Custom URLs

Mix router-generated URLs with custom URL patterns for maximum flexibility:

# urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from . import views

# Router for ViewSets
router = DefaultRouter()
router.register(r'books', views.BookViewSet, basename='book')
router.register(r'authors', views.AuthorViewSet, basename='author')

# Custom URL patterns
urlpatterns = [
    # Custom function-based views
    path('api/search/', views.search_books, name='search'),
    path('api/stats/', views.get_statistics, name='stats'),
    
    # Custom class-based views
    path('api/featured/', views.FeaturedBooksView.as_view(), name='featured'),
    
    # Include router URLs
    path('api/', include(router.urls)),
]

Final URL Structure:

/api/search/            - Custom search endpoint
/api/stats/             - Custom statistics endpoint
/api/featured/          - Custom featured books
/api/books/             - Router-generated book endpoints
/api/authors/           - Router-generated author endpoints

🔹 Nested Routers

Create nested URL structures for related resources using custom registration:

# urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from . import views

router = DefaultRouter()
router.register(r'authors', views.AuthorViewSet, basename='author')
router.register(r'books', views.BookViewSet, basename='book')

# Nested routes for author's books
urlpatterns = [
    path('api/', include(router.urls)),
    path('api/authors//books/', 
         views.AuthorBooksView.as_view(), 
         name='author-books'),
]
# views.py
class AuthorBooksView(generics.ListAPIView):
    serializer_class = BookSerializer
    
    def get_queryset(self):
        author_id = self.kwargs['author_id']
        return Book.objects.filter(author_id=author_id)

Nested URL Example:

GET /api/authors/1/books/  - All books by author with ID 1

🔹 API Versioning with Routers

Organize different API versions using separate routers for backward compatibility:

# urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from . import views

# Version 1 Router
router_v1 = DefaultRouter()
router_v1.register(r'books', views.BookViewSetV1, basename='book')

# Version 2 Router
router_v2 = DefaultRouter()
router_v2.register(r'books', views.BookViewSetV2, basename='book')
router_v2.register(r'authors', views.AuthorViewSet, basename='author')

urlpatterns = [
    path('api/v1/', include(router_v1.urls)),
    path('api/v2/', include(router_v2.urls)),
]

Versioned API Structure:

/api/v1/books/          - Version 1 books endpoint
/api/v2/books/          - Version 2 books endpoint
/api/v2/authors/        - Version 2 authors endpoint (new)

🧠 Test Your Knowledge

Which router includes an API root view?