SyntaxStudy
Sign Up
Django ModelViewSet, Routers, and DRF Authentication
Django Beginner 1 min read

ModelViewSet, Routers, and DRF Authentication

ModelViewSet is DRF's highest-level abstraction. A single ModelViewSet class handles list, create, retrieve, update, partial_update, and destroy operations. Combined with a Router, it automatically generates all the URL patterns needed for a fully RESTful resource endpoint. Routers map the viewset actions to the appropriate HTTP methods and URL patterns. Permissions in DRF control who can access each API endpoint. The IsAuthenticated permission requires a valid authenticated user. IsAuthenticatedOrReadOnly allows GET, HEAD, and OPTIONS for everyone but requires authentication for write operations. IsAdminUser restricts access to staff users. Custom permissions are classes with has_permission() and has_object_permission() methods, which give you fine-grained control over individual object access. DRF supports multiple authentication schemes. SessionAuthentication uses Django's session cookies, which is appropriate for browser-based AJAX clients. TokenAuthentication uses a token in the Authorization header (Token ), suitable for mobile and third-party API clients. For production APIs, JWT (JSON Web Token) authentication via djangorestframework-simplejwt is popular because tokens carry user claims and don't require database lookups.
Example
# blog/api/views.py — ModelViewSet
from rest_framework import viewsets, permissions
from rest_framework.decorators import action
from rest_framework.response import Response
from ..models import Post
from ..serializers import PostSerializer


class PostViewSet(viewsets.ModelViewSet):
    queryset           = Post.objects.select_related('author').order_by('-published_at')
    serializer_class   = PostSerializer
    permission_classes = [permissions.IsAuthenticatedOrReadOnly]

    def get_queryset(self):
        qs = super().get_queryset()
        if self.action == 'list':
            qs = qs.filter(status=Post.STATUS_PUBLISHED)
        return qs

    def perform_create(self, serializer):
        serializer.save(author=self.request.user)

    # Custom action: GET /api/posts/my/
    @action(detail=False, methods=['get'], permission_classes=[permissions.IsAuthenticated])
    def my(self, request):
        posts = Post.objects.filter(author=request.user)
        serializer = self.get_serializer(posts, many=True)
        return Response(serializer.data)


# blog/api/urls.py — router-based URL registration
from rest_framework.routers import DefaultRouter
from . import views

router = DefaultRouter()
router.register(r'posts', views.PostViewSet, basename='post')

urlpatterns = router.urls
# Generated URLs:
# GET/POST  /api/posts/        -> list / create
# GET       /api/posts/my/     -> custom action
# GET/PUT/PATCH/DELETE /api/posts/{pk}/  -> detail operations

# mysite/settings.py — DRF default settings
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.TokenAuthentication',
    ],
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticatedOrReadOnly',
    ],
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 20,
}