Views & URLs#
ViewSets (Recommended - 80% use case)#
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