Resource routing allows you to quickly declare all of the common routes for a given resourceful controller. Instead of declaring separate routes for your index... a resourceful route declares them in a single line of code.
Some Web frameworks such as Rails provide functionality for automatically determining how the URLs for an application should be mapped to the logic that deals with handling incoming requests.
REST framework adds support for automatic URL routing to Django, and provides you with a simple, quick and consistent way of wiring your view logic to a set of URLs.
Here's an example of a simple URL conf, that usesSimpleRouter.
from rest_framework import routersrouter = routers.SimpleRouter()router.register(r'users', UserViewSet)router.register(r'accounts', AccountViewSet)urlpatterns = router.urlsThere are two mandatory arguments to theregister() method:
prefix - The URL prefix to use for this set of routes.viewset - The viewset class.Optionally, you may also specify an additional argument:
basename - The base to use for the URL names that are created. If unset the basename will be automatically generated based on thequeryset attribute of the viewset, if it has one. Note that if the viewset does not include aqueryset attribute then you must setbasename when registering the viewset.The example above would generate the following URL patterns:
^users/$ Name:'user-list'^users/{pk}/$ Name:'user-detail'^accounts/$ Name:'account-list'^accounts/{pk}/$ Name:'account-detail'Note
Thebasename argument is used to specify the initial part of the view name pattern. In the example above, that's theuser oraccount part.
Typically you won'tneed to specify thebasename argument, but if you have a viewset where you've defined a customget_queryset method, then the viewset may not have a.queryset attribute set. If you try to register that viewset you'll see an error like this:
'basename' argument not specified, and could not automatically determine the name from the viewset, as it does not have a '.queryset' attribute.This means you'll need to explicitly set thebasename argument when registering the viewset, as it could not be automatically determined from the model name.
include with routersThe.urls attribute on a router instance is simply a standard list of URL patterns. There are a number of different styles for how you can include these URLs.
For example, you can appendrouter.urls to a list of existing views...
router = routers.SimpleRouter()router.register(r'users', UserViewSet)router.register(r'accounts', AccountViewSet)urlpatterns = [ path('forgot-password/', ForgotPasswordFormView.as_view()),]urlpatterns += router.urlsAlternatively you can use Django'sinclude function, like so...
urlpatterns = [ path('forgot-password', ForgotPasswordFormView.as_view()), path('', include(router.urls)),]You may useinclude with an application namespace:
urlpatterns = [ path('forgot-password/', ForgotPasswordFormView.as_view()), path('api/', include((router.urls, 'app_name'))),]Or both an application and instance namespace:
urlpatterns = [ path('forgot-password/', ForgotPasswordFormView.as_view()), path('api/', include((router.urls, 'app_name'), namespace='instance_name')),]See Django'sURL namespaces docs and theinclude API reference for more details.
Note
If using namespacing with hyperlinked serializers you'll also need to ensure that anyview_name parameterson the serializers correctly reflect the namespace. In the examples above you'd need to include a parameter such asview_name='app_name:user-detail' for serializer fields hyperlinked to the user detail view.
The automaticview_name generation uses a pattern like%(model_name)-detail. Unless your models names actually clashyou may be better offnot namespacing your Django REST Framework views when using hyperlinked serializers.
A viewset maymark extra actions for routing by decorating a method with the@action decorator. These extra actions will be included in the generated routes. For example, given theset_password method on theUserViewSet class:
from myapp.permissions import IsAdminOrIsSelffrom rest_framework.decorators import actionclass UserViewSet(ModelViewSet): ... @action(methods=['post'], detail=True, permission_classes=[IsAdminOrIsSelf]) def set_password(self, request, pk=None): ...The following route would be generated:
^users/{pk}/set_password/$'user-set-password'By default, the URL pattern is based on the method name, and the URL name is the combination of theViewSet.basename and the hyphenated method name.If you don't want to use the defaults for either of these values, you can instead provide theurl_path andurl_name arguments to the@action decorator.
For example, if you want to change the URL for our custom action to^users/{pk}/change-password/$, you could write:
from myapp.permissions import IsAdminOrIsSelffrom rest_framework.decorators import actionclass UserViewSet(ModelViewSet): ... @action(methods=['post'], detail=True, permission_classes=[IsAdminOrIsSelf], url_path='change-password', url_name='change_password') def set_password(self, request, pk=None): ...The above example would now generate the following URL pattern:
^users/{pk}/change-password/$'user-change_password'path() with routersBy default, the URLs created by routers use regular expressions. This behavior can be modified by setting theuse_regex_path argument toFalse when instantiating the router, in this casepath converters are used. For example:
router = SimpleRouter(use_regex_path=False)The router will match lookup values containing any characters except slashes and period characters. For a more restrictive (or lenient) lookup pattern, set thelookup_value_regex attribute on the viewset orlookup_value_converter if using path converters. For example, you can limit the lookup to valid UUIDs:
class MyModelViewSet(mixins.RetrieveModelMixin, viewsets.GenericViewSet): lookup_field = 'my_model_id' lookup_value_regex = '[0-9a-f]{32}'class MyPathModelViewSet(mixins.RetrieveModelMixin, viewsets.GenericViewSet): lookup_field = 'my_model_uuid' lookup_value_converter = 'uuid'Note that path converters will be used on all URLs registered in the router, including viewset actions.
This router includes routes for the standard set oflist,create,retrieve,update,partial_update anddestroy actions. The viewset can also mark additional methods to be routed, using the@action decorator.
| URL Style | HTTP Method | Action | URL Name |
|---|---|---|---|
| {prefix}/ | GET | list | {basename}-list |
| POST | create | ||
| {prefix}/{url_path}/ | GET, or as specified by `methods` argument | `@action(detail=False)` decorated method | {basename}-{url_name} |
| {prefix}/{lookup}/ | GET | retrieve | {basename}-detail |
| PUT | update | ||
| PATCH | partial_update | ||
| DELETE | destroy | ||
| {prefix}/{lookup}/{url_path}/ | GET, or as specified by `methods` argument | `@action(detail=True)` decorated method | {basename}-{url_name} |
By default, the URLs created bySimpleRouter are appended with a trailing slash.This behavior can be modified by setting thetrailing_slash argument toFalse when instantiating the router. For example:
router = SimpleRouter(trailing_slash=False)Trailing slashes are conventional in Django, but are not used by default in some other frameworks such as Rails. Which style you choose to use is largely a matter of preference, although some javascript frameworks may expect a particular routing style.
This router is similar toSimpleRouter as above, but additionally includes a default API root view, that returns a response containing hyperlinks to all the list views. It also generates routes for optional.json style format suffixes.
| URL Style | HTTP Method | Action | URL Name |
|---|---|---|---|
| [.format] | GET | automatically generated root view | api-root |
| {prefix}/[.format] | GET | list | {basename}-list |
| POST | create | ||
| {prefix}/{url_path}/[.format] | GET, or as specified by `methods` argument | `@action(detail=False)` decorated method | {basename}-{url_name} |
| {prefix}/{lookup}/[.format] | GET | retrieve | {basename}-detail |
| PUT | update | ||
| PATCH | partial_update | ||
| DELETE | destroy | ||
| {prefix}/{lookup}/{url_path}/[.format] | GET, or as specified by `methods` argument | `@action(detail=True)` decorated method | {basename}-{url_name} |
As withSimpleRouter the trailing slashes on the URL routes can be removed by setting thetrailing_slash argument toFalse when instantiating the router.
router = DefaultRouter(trailing_slash=False)Implementing a custom router isn't something you'd need to do very often, but it can be useful if you have specific requirements about how the URLs for your API are structured. Doing so allows you to encapsulate the URL structure in a reusable way that ensures you don't have to write your URL patterns explicitly for each new view.
The simplest way to implement a custom router is to subclass one of the existing router classes. The.routes attribute is used to template the URL patterns that will be mapped to each viewset. The.routes attribute is a list ofRoute named tuples.
The arguments to theRoute named tuple are:
url: A string representing the URL to be routed. May include the following format strings:
{prefix} - The URL prefix to use for this set of routes.{lookup} - The lookup field used to match against a single instance.{trailing_slash} - Either a '/' or an empty string, depending on thetrailing_slash argument.mapping: A mapping of HTTP method names to the view methods
name: The name of the URL as used inreverse calls. May include the following format string:
{basename} - The base to use for the URL names that are created.initkwargs: A dictionary of any additional arguments that should be passed when instantiating the view. Note that thedetail,basename, andsuffix arguments are reserved for viewset introspection and are also used by the browsable API to generate the view name and breadcrumb links.
You can also customize how the@action decorator is routed. Include theDynamicRoute named tuple in the.routes list, setting thedetail argument as appropriate for the list-based and detail-based routes. In addition todetail, the arguments toDynamicRoute are:
url: A string representing the URL to be routed. May include the same format strings asRoute, and additionally accepts the{url_path} format string.
name: The name of the URL as used inreverse calls. May include the following format strings:
{basename} - The base to use for the URL names that are created.{url_name} - Theurl_name provided to the@action.initkwargs: A dictionary of any additional arguments that should be passed when instantiating the view.
The following example will only route to thelist andretrieve actions, and does not use the trailing slash convention.
from rest_framework.routers import Route, DynamicRoute, SimpleRouterclass CustomReadOnlyRouter(SimpleRouter): """ A router for read-only APIs, which doesn't use trailing slashes. """ routes = [ Route( url=r'^{prefix}$', mapping={'get': 'list'}, name='{basename}-list', detail=False, initkwargs={'suffix': 'List'} ), Route( url=r'^{prefix}/{lookup}$', mapping={'get': 'retrieve'}, name='{basename}-detail', detail=True, initkwargs={'suffix': 'Detail'} ), DynamicRoute( url=r'^{prefix}/{lookup}/{url_path}$', name='{basename}-{url_name}', detail=True, initkwargs={} ) ]Let's take a look at the routes ourCustomReadOnlyRouter would generate for a simple viewset.
views.py:
class UserViewSet(viewsets.ReadOnlyModelViewSet): """ A viewset that provides the standard actions """ queryset = User.objects.all() serializer_class = UserSerializer lookup_field = 'username' @action(detail=True) def group_names(self, request, pk=None): """ Returns a list of all the group names that the given user belongs to. """ user = self.get_object() groups = user.groups.all() return Response([group.name for group in groups])urls.py:
router = CustomReadOnlyRouter()router.register('users', UserViewSet)urlpatterns = router.urlsThe following mappings would be generated...
| URL | HTTP Method | Action | URL Name |
|---|---|---|---|
| /users | GET | list | user-list |
| /users/{username} | GET | retrieve | user-detail |
| /users/{username}/group_names | GET | group_names | user-group-names |
For another example of setting the.routes attribute, see the source code for theSimpleRouter class.
If you want to provide totally custom behavior, you can overrideBaseRouter and override theget_urls(self) method. The method should inspect the registered viewsets and return a list of URL patterns. The registered prefix, viewset and basename tuples may be inspected by accessing theself.registry attribute.
You may also want to override theget_default_basename(self, viewset) method, or else always explicitly set thebasename argument when registering your viewsets with the router.
The following third party packages are also available.
Thedrf-nested-routers package provides routers and relationship fields for working with nested resources.
Thewq.db package provides an advancedModelRouter class (and singleton instance) that extendsDefaultRouter with aregister_model() API. Much like Django'sadmin.site.register, the only required argument torest.router.register_model is a model class. Reasonable defaults for a url prefix, serializer, and viewset will be inferred from the model and global configuration.
from wq.db import restfrom myapp.models import MyModelrest.router.register_model(MyModel)TheDRF-extensions package providesrouters for creatingnested viewsets,collection level controllers withcustomizable endpoint names.