Is there any common pattern for routing APIViews
(ie, not ViewSets
) with Django Rest Framework?
For example, if I've got an API View that takes an optional argument:
class ReportView(APIView): def get(self, request, report_name=None): # … stuff … return Response(report)
Is there any standard pattern — apart from writing out a standard Django URL router — for routing that view?
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.
Django views facilitate processing the HTTP requests and providing HTTP responses. On receiving an HTTP request, Django creates an HttpRequest instance, and it is passed as the first argument to the view function. This instance contains HTTP verbs such as GET, POST, PUT, PATCH, or DELETE.
APIView allow us to define functions that match standard HTTP methods like GET, POST, PUT, PATCH, etc. Viewsets allow us to define functions that match to common API object actions like : LIST, CREATE, RETRIEVE, UPDATE, etc.
A ViewSet class is simply a type of class-based View, that does not provide any method handlers such as . get() or . post() , and instead provides actions such as . list() and . create() .
Being able to add simple API views is quite useful for displaying the urls in root-api
view.
Here is the simplest extension of DefaultRouter
that enables registering not only viewsets but also simple API views:
from django.conf.urls import url from rest_framework import routers, viewsets from rest_framework.urlpatterns import format_suffix_patterns class DefaultRouterWithSimpleViews(routers.DefaultRouter): """ Extends functionality of DefaultRouter adding possibility to register simple API views, not just Viewsets. """ def get_routes(self, viewset): """ Checks if the viewset is an instance of ViewSet, otherwise assumes it's a simple view and does not run original `get_routes` code. """ if issubclass(viewset, viewsets.ViewSetMixin): return super(DefaultRouterWithSimpleViews, self).get_routes(viewset) return [] def get_urls(self): """ Append non-viewset views to the urls generated by the original `get_urls` method. """ # URLs for simple views ret = [] for prefix, viewset, basename in self.registry: # Skip viewsets if issubclass(viewset, viewsets.ViewSetMixin): continue # URL regex regex = '{prefix}{trailing_slash}$'.format( prefix=prefix, trailing_slash=self.trailing_slash ) # The view name has to have suffix "-list" due to specifics # of the DefaultRouter implementation. ret.append(url( regex, viewset.as_view(), name='{0}-list'.format(basename) )) # Format suffixes ret = format_suffix_patterns(ret, allowed=['json', 'html']) # Prepend URLs for viewsets and return return super(DefaultRouterWithSimpleViews, self).get_urls() + ret
Now you can use simple Django views alongside with the rest framework ViewSets:
router = DefaultRouterWithSimpleViews() router.register(r'users', UserViewSet, 'users') # <- Viewset! router.register(r'reset-pwd', ResetPasswordView, 'reset_pwd') # <- Simple view! urlpatterns = router.urls
Update: Added support of format suffixes (thanks to alexander-klimenko)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With