Skip to content

Views & URLs#

ModelViewSet (Full CRUD)#

# myapp/views.py
from rest_framework import viewsets
from .models import Post
from .serializers import PostSerializer

class PostViewSet(viewsets.ModelViewSet):
    queryset = Post.objects.all()
    serializer_class = PostSerializer
    permission_classes = [IsAuthenticated]  # Optional

Auto-generates: - GET /api/posts/ - List - POST /api/posts/ - Create - GET /api/posts/{id}/ - Retrieve - PUT /api/posts/{id}/ - Update (full) - PATCH /api/posts/{id}/ - Update (partial) - DELETE /api/posts/{id}/ - Delete

ReadOnlyModelViewSet#

class PostViewSet(viewsets.ReadOnlyModelViewSet):
    queryset = Post.objects.all()
    serializer_class = PostSerializer
    # Only GET operations (list, retrieve)

Custom Actions#

from rest_framework.decorators import action
from rest_framework.response import Response

class PostViewSet(viewsets.ModelViewSet):
    queryset = Post.objects.all()
    serializer_class = PostSerializer

    # Custom action (GET /api/posts/{id}/publish/)
    @action(detail=True, methods=['post'])
    def publish(self, request, pk=None):
        post = self.get_object()
        post.is_published = True
        post.save()
        return Response({'status': 'published'})

    # List action (GET /api/posts/published/)
    @action(detail=False)
    def published(self, request):
        posts = self.queryset.filter(is_published=True)
        serializer = self.get_serializer(posts, many=True)
        return Response(serializer.data)

APIView (More control)#

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status

class PostListAPIView(APIView):
    def get(self, request):
        posts = Post.objects.all()
        serializer = PostSerializer(posts, many=True)
        return Response(serializer.data)

    def post(self, request):
        serializer = PostSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

class PostDetailAPIView(APIView):
    def get(self, request, pk):
        post = get_object_or_404(Post, pk=pk)
        serializer = PostSerializer(post)
        return Response(serializer.data)

    def put(self, request, pk):
        post = get_object_or_404(Post, pk=pk)
        serializer = PostSerializer(post, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    def delete(self, request, pk):
        post = get_object_or_404(Post, pk=pk)
        post.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

Generic Views#

from rest_framework import generics

# List and Create
class PostListCreateView(generics.ListCreateAPIView):
    queryset = Post.objects.all()
    serializer_class = PostSerializer

# Retrieve, Update, Delete
class PostRetrieveUpdateDestroyView(generics.RetrieveUpdateDestroyAPIView):
    queryset = Post.objects.all()
    serializer_class = PostSerializer

Routers#

Default Router#

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

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

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

# Generated URLs:
# /api/posts/ - list, create
# /api/posts/{id}/ - retrieve, update, delete
# /api/posts/{id}/publish/ - custom action

Simple Router#

from rest_framework.routers import SimpleRouter

router = SimpleRouter()
router.register(r'posts', PostViewSet)
# Same as DefaultRouter but without .json/.api extensions

Nested Routers#

pip install drf-nested-routers
# myapp/urls.py
from rest_framework_nested import routers
from .views import PostViewSet, CommentViewSet

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

# Nested router for comments
posts_router = routers.NestedDefaultRouter(router, r'posts', lookup='post')
posts_router.register(r'comments', CommentViewSet, basename='post-comments')

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

# Generated URLs:
# /api/posts/ - posts list
# /api/posts/{post_id}/comments/ - comments for post
# /api/posts/{post_id}/comments/{comment_id}/ - comment detail
# myapp/views.py
class CommentViewSet(viewsets.ModelViewSet):
    serializer_class = CommentSerializer

    def get_queryset(self):
        return Comment.objects.filter(post_id=self.kwargs['post_pk'])

    def perform_create(self, serializer):
        serializer.save(post_id=self.kwargs['post_pk'])

URL Patterns (Manual)#

# myapp/urls.py
from django.urls import path
from .views import PostListAPIView, PostDetailAPIView

urlpatterns = [
    path('api/posts/', PostListAPIView.as_view(), name='post-list'),
    path('api/posts/<int:pk>/', PostDetailAPIView.as_view(), name='post-detail'),
]

Override ViewSet Methods#

class PostViewSet(viewsets.ModelViewSet):
    queryset = Post.objects.all()
    serializer_class = PostSerializer

    def get_queryset(self):
        # Filter by user
        if self.request.user.is_staff:
            return Post.objects.all()
        return Post.objects.filter(author=self.request.user)

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

    def perform_update(self, serializer):
        # Custom update logic
        serializer.save(updated_by=self.request.user)

    def get_serializer_class(self):
        # Different serializer for different actions
        if self.action == 'list':
            return PostListSerializer
        return PostSerializer

Next: RESTful API