Fun with Learning Technology
LearnCoursesQuestionsTracksToolsNewsExplorePractice
Fun with Learning Technology

A new problem, explained clearly, every day.

Subscribe
Learn
  • Lessons
  • Topics
  • News
  • Tools
  • Courses
  • Career tracks
  • Everything
Site
  • About
  • Contact
  • Support
  • Privacy
  • Terms
Get the daily one

One email per new problem. No spam.

Request a tutorial

Requests shape what gets made next.

© 2026 Fun with Learning TechnologyRSS
Home›Courses›Django›Routers

Django REST Framework

Routers

Routers in Django REST Framework automate the generation of URL patterns for ViewSets, mapping them to standard HTTP operations. They eliminate the repetitive boilerplate code associated with manual URL configuration and ensure consistent routing conventions across an API. You should implement routers whenever you are building a CRUD-heavy resource-oriented API to drastically improve maintainability and speed of development.

The Mechanism of Automatic URL Generation

At the core of the Django REST Framework router system is a mechanism that inspects your ViewSet to determine which actions (like list, create, retrieve, update, destroy) are supported. When you register a ViewSet with a router, the router analyzes the class structure, looking for methods that correspond to these standard operations. By observing the available methods in the ViewSet, the router dynamically constructs the appropriate URL patterns. This works because DRF conventions dictate that specific method names map directly to specific HTTP verbs and URL path structures. By centralizing this logic, the router ensures that the generated URL structure is predictable and RESTful by default. This abstraction allows the developer to focus on the business logic inside the ViewSet rather than the tedious task of defining every individual path pattern manually. Consequently, the router acts as a reflection of the ViewSet's capabilities, ensuring that your URL table stays perfectly synchronized with your view logic without requiring manual synchronization of two separate files.

from rest_framework import routers
from my_app.views import UserViewSet

# Instantiate the DefaultRouter which provides API root and format suffixes
router = routers.DefaultRouter()

# Register the ViewSet with a base path prefix 'users'
# The router creates: /users/, /users/{pk}/
router.register(r'users', UserViewSet)

# The router's urls attribute contains the generated URL patterns list
urlpatterns = router.urls

DefaultRouter vs SimpleRouter

Choosing between DefaultRouter and SimpleRouter depends primarily on the level of functionality you require from your generated URL patterns. The SimpleRouter is the leaner version, generating basic URL patterns for resource collection and individual resource instances, but it does not provide an API root view or built-in support for format suffixes. Conversely, the DefaultRouter builds upon the foundation of SimpleRouter but includes a significant enhancement: it generates an API root view that returns a browsable index of all registered endpoints when accessed at the base URL. This is invaluable during development because it allows you to visualize and navigate your entire API surface area through a web interface. The DefaultRouter also automatically appends a format suffix to URLs, allowing clients to request specific data representations like JSON or HTML by adding an extension to the URL. These additions make the DefaultRouter the standard choice for most production projects where API discoverability and flexibility are prioritized.

from rest_framework import routers

# SimpleRouter is minimal; use it when you don't need a root index
simple = routers.SimpleRouter()
simple.register(r'books', BookViewSet)

# DefaultRouter provides an API root and handles file extensions (.json)
default = routers.DefaultRouter()
default.register(r'books', BookViewSet)

# Both objects provide .urls to be included in your main urls.py
urlpatterns = default.urls

Routing for Custom Actions

A powerful feature of routers is their ability to automatically generate URL routes for custom methods marked with the @action decorator. When you add a custom method to your ViewSet to perform a task outside of the standard CRUD operations, such as a bulk status update or an object-specific report, you must define its URL routing behavior. By decorating the method, you tell the router to include it in the URL map. The router examines the 'methods' and 'detail' arguments provided to the decorator. If 'detail' is True, the router appends the action name as a sub-path under the specific instance URL, such as /books/{pk}/publish/. If 'detail' is False, it adds the action to the collection URL, such as /books/archive/. This automatic discovery mechanism ensures that custom functionality remains integrated into the same routing logic as your standard CRUD methods, maintaining a clean and unified URL structure for your API endpoints.

from rest_framework.decorators import action
from rest_framework import viewsets

class BookViewSet(viewsets.ModelViewSet):
    queryset = Book.objects.all()
    
    # This creates the route: /books/{pk}/publish/
    @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'})

# Router automatically detects this 'publish' action.

Overriding Base Names

Sometimes, a ViewSet might not be explicitly tied to a standard queryset, or you might have multiple ViewSets that share the same base model. In such scenarios, the router may struggle to determine the appropriate 'basename' used to name the URL patterns. The basename is critical because it is used for reverse URL lookup; if two ViewSets register with the same name, the router will throw an error. When you encounter this, or when you simply want to provide a specific string for your URL names, you must pass the 'basename' argument during registration. By explicitly setting the basename, you inform the router of the core identifier for that route, allowing for unambiguous URL resolution throughout your application. This is particularly useful when using custom querysets that do not share the exact model name or when organizing large APIs where explicit control over naming conventions is preferred to avoid potential namespace collisions in your URL reverse-resolution process.

from rest_framework import routers
from my_app.views import CustomReportViewSet

router = routers.DefaultRouter()

# Providing basename explicitly prevents errors when no queryset is defined
# or when multiple routers might conflict over naming
router.register(r'reports', CustomReportViewSet, basename='report')

# Now you can reverse('report-list') in your code safely.

Nesting Routes for Relationships

When your data model has clear hierarchical relationships, such as authors and their books, you often want your URL structure to reflect this nesting. While routers are primarily designed for top-level resources, you can achieve nested routing by registering a router's URLs within a specific path. By including the router's URL list inside a standard Django path function, you effectively scope the resource under a prefix. This pattern is fundamental to designing clean APIs where the path accurately represents the resource's relationship to its parent. Because the router generates its own collection of path patterns, and the Django path system simply consumes that list, you can seamlessly integrate multiple routers to create a complex, tree-like structure. This modular approach allows you to organize your API into logical segments, ensuring that resource hierarchies are not only easy for developers to reason about but are also intuitively mapped for the clients consuming your service.

from django.urls import path, include
from rest_framework import routers

# Create a sub-router for nested items
books_router = routers.DefaultRouter()
books_router.register(r'books', BookViewSet)

urlpatterns = [
    # Nesting the books under authors: /authors/{pk}/books/
    path('authors/<int:pk>/', include(books_router.urls)),
    path('categories/', include(category_router.urls))
]

Key points

  • Routers automate the creation of URL patterns for ViewSets based on standard CRUD method names.
  • The DefaultRouter provides an API root view and support for format suffixes like .json.
  • SimpleRouter is a lightweight alternative that avoids adding the API root view.
  • Custom actions in ViewSets are automatically routed when decorated with @action.
  • The basename argument is required when the router cannot infer a name from the ViewSet's queryset.
  • Routers reduce boilerplate code and ensure consistent API URL conventions across the project.
  • Nested routing can be achieved by including a router's URL collection within a standard path.
  • Routers map URL patterns to the internal actions of a ViewSet based on HTTP verbs.

Common mistakes

  • Mistake: Manually creating URLs for DRF ViewSets. Why it's wrong: DRF Routers are designed to automate this process to ensure consistency. Fix: Use a SimpleRouter or DefaultRouter to register your ViewSet instead of writing 'path()' manually.
  • Mistake: Forgetting to include the router's URLs in the main urls.py. Why it's wrong: The router object is not automatically included in the URL configuration. Fix: Add 'path('', include(router.urls))' to your project's urlpatterns.
  • Mistake: Overusing DefaultRouter when SimpleRouter is sufficient. Why it's wrong: DefaultRouter adds a root view and a format suffix pattern which might not be needed. Fix: Use SimpleRouter for cleaner URL patterns unless you specifically need the API root page.
  • Mistake: Confusing the 'basename' argument in 'router.register'. Why it's wrong: Django needs the basename to generate URL names if the ViewSet doesn't provide a queryset. Fix: Always include 'basename' when registering a ViewSet that lacks a queryset attribute.
  • Mistake: Nesting routers unnecessarily. Why it's wrong: Deeply nested resources create complex, difficult-to-maintain URL structures. Fix: Keep resource nesting to a maximum of one level and handle deeper filtering via query parameters.

Interview questions

What is the primary purpose of a Router in Django REST Framework?

In Django REST Framework, a Router is a tool used to automatically generate URL patterns for your API views. Its primary purpose is to simplify the configuration of your URL patterns, preventing the need to manually define every single path. By using a router, you ensure that your URL structure remains consistent and follows RESTful conventions, which helps keep the codebase maintainable and readable as your API grows in complexity.

How do you implement a SimpleRouter in a basic Django application?

To implement a SimpleRouter, you first import it from 'rest_framework.routers'. Then, you instantiate it in your 'urls.py' file. You register your viewsets with the router using 'router.register(r'prefix', ViewSetClass)'. Finally, you include the 'router.urls' in your 'urlpatterns' list. This automatically maps standard actions like list, create, retrieve, update, and destroy to the correct URL paths without you having to write manual regex patterns for each operation.

What is the difference between DefaultRouter and SimpleRouter in Django?

The main difference between DefaultRouter and SimpleRouter is that DefaultRouter provides an automatically generated API root view, which lists all available endpoints in a browsable interface. Furthermore, DefaultRouter supports an optional format suffix pattern for URLs, such as '.json' or '.html'. SimpleRouter is more minimalist, providing only the necessary routes for your viewsets without the additional API root view or the extra URL mapping overhead found in DefaultRouter.

Compare the approach of using Routers versus manual URL configuration with 'path' and 're_path'.

Using Routers provides automated, standard URL generation that enforces consistent RESTful design, which is highly efficient for ViewSets. In contrast, manual URL configuration using 'path' or 're_path' offers granular control over every specific endpoint. While routers reduce boilerplate code and potential errors in large APIs, manual configuration is necessary when your API requires highly custom URL structures or complex logic that does not fit the standard CRUD-based pattern of Django viewsets.

How can you override the URL paths generated by a Django Router?

You can override default URL paths by using the 'basename' argument when registering your viewset, or by manually defining extra routes using the '@action' decorator within your ViewSet. If you need a completely custom URL structure, you can bypass the router entirely for that specific route and define it manually in your 'urlpatterns' list before including the router's auto-generated paths. This allows for flexibility while keeping the majority of your API endpoints clean and automated.

Explain how Django Routers handle nested resources or relationships.

Django Routers do not natively handle deeply nested resources out of the box because RESTful design principles suggest flat URL structures. To handle nested relationships, developers typically use third-party libraries like 'drf-nested-routers'. These libraries extend the functionality of the standard Router to allow paths such as 'users/{user_pk}/posts/{pk}/'. You define these by registering child routes to parent routes, creating hierarchical URL patterns that logically reflect the database relationship structure defined in your Django models.

All Django interview questions →

Check yourself

1. What is the primary advantage of using a Router in Django REST Framework compared to standard path definitions?

  • A.It provides automatic URL pattern generation based on ViewSet method names
  • B.It improves the database query performance of the associated view
  • C.It forces the use of JSON as the only supported content type
  • D.It automatically secures all endpoints with token authentication
Show answer

A. It provides automatic URL pattern generation based on ViewSet method names
Routers map ViewSet methods (list, create, retrieve, etc.) to URL patterns automatically. Option 1 is wrong because routers don't affect DB performance. Option 2 is wrong because content negotiation is handled by parsers/renderers. Option 4 is wrong because authentication is handled by permission classes.

2. When registering a ViewSet with a Router, why would you need to provide a 'basename' argument?

  • A.To define the database table name that the view accesses
  • B.To ensure the URL reversing system can generate unique names for the patterns
  • C.To override the default HTTP methods permitted for the view
  • D.To specify the primary key field for the model
Show answer

B. To ensure the URL reversing system can generate unique names for the patterns
If a ViewSet doesn't have a queryset attribute, the router needs 'basename' to form the prefix for URL names (e.g., 'user-list'). The other options describe model or view configuration that exists independently of the router.

3. What is the key difference between SimpleRouter and DefaultRouter?

  • A.SimpleRouter supports versioning while DefaultRouter does not
  • B.DefaultRouter includes an API root view and 'format' suffix support
  • C.SimpleRouter is only for function-based views
  • D.DefaultRouter requires a database migration to function
Show answer

B. DefaultRouter includes an API root view and 'format' suffix support
DefaultRouter adds an automatically generated API root and supports .json/.html format suffixes. SimpleRouter lacks these. The other options are incorrect as SimpleRouter works with ViewSets and neither requires specific migrations.

4. If you have a ViewSet and want to add an extra action with a specific URL, which decorator should you use, and how does the Router react?

  • A.Use @action; the router will automatically generate a URL for it
  • B.Use @method; the router ignores it and requires manual path definitions
  • C.Use @detail_route; the router will hide the action from the public API
  • D.Use @custom; the router will block all requests to that endpoint
Show answer

A. Use @action; the router will automatically generate a URL for it
The @action decorator marks a method to be included in the router's URL generation. 'Method' and 'custom' are not standard decorators for this purpose, and 'detail_route' is deprecated/renamed, not a security block.

5. How do you include the URL patterns generated by a router into the main project urls.py?

  • A.By passing the router object directly to the 'urlpatterns' list
  • B.By calling 'router.generate_urls()' and passing the result to 'include()'
  • C.By adding the 'router.urls' list to the 'urlpatterns' list
  • D.By importing 'router_patterns' from the rest_framework.routers module
Show answer

C. By adding the 'router.urls' list to the 'urlpatterns' list
Routers expose a 'urls' attribute which is a list of URL patterns that can be appended to the 'urlpatterns' list. The other options suggest syntax that does not exist or is incorrect for Django's URL configuration.

Take the full Django quiz →

← PreviousAPIView vs ViewSetsNext →Authentication in DRF — JWT

Django

30 lessons, free to read.

All lessons →

Track your progress

Sign in to mark lessons done, score quizzes and keep notes.

Open in the app