python / expert
Snippet
Django REST Framework ViewSet with Custom Actions and Permission Classes
This snippet demonstrates Django REST Framework ViewSet customization with dynamic permission handling and custom actions. The ViewSet implements action-specific permissions where retrieve and list are public, archive and stats require membership, while destroy operations need admin rights. Custom actions like archive, stats, and bulk_archive provide resource-specific endpoints with proper permission enforcement.
snippet.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
from rest_framework import viewsets, permissions, statusfrom rest_framework.decorators import actionfrom rest_framework.response import Responsefrom rest_framework.views import APIViewfrom django.shortcuts import get_object_or_404from myapp.models import Project, Membershipfrom myapp.serializers import ProjectSerializer, ProjectStatsSerializerfrom myapp.permissions import IsProjectMember, IsProjectAdminclass ProjectViewSet(viewsets.ModelViewSet):queryset = Project.objects.all()serializer_class = ProjectSerializerpermission_classes = [permissions.IsAuthenticated]def get_permissions(self):if self.action in ['retrieve', 'list']:return [permissions.AllowAny()]elif self.action in ['destroy', 'partial_update']:return [IsProjectAdmin()]elif self.action in ['archive', 'stats']:return [IsProjectMember()]return [permissions.IsAuthenticated()]def get_queryset(self):user = self.request.userif self.action == 'list' and not self.request.user.is_authenticated:return Project.objects.filter(is_public=True)return super().get_queryset()@action(detail=True, methods=['post'], permission_classes=[IsProjectAdmin])def archive(self, request, pk=None):project = self.get_object()project.is_archived = Trueproject.save()return Response({'status': 'archived'}, status=status.HTTP_200_OK)@action(detail=True, methods=['get'], url_path='statistics')def stats(self, request, pk=None):project = self.get_object()data = {'members_count': project.membership_set.count(),'tasks_completed': project.tasks.filter(is_completed=True).count(),'total_tasks': project.tasks.count(),}serializer = ProjectStatsSerializer(data)return Response(serializer.data)@action(detail=False, methods=['post'])def bulk_archive(self, request):ids = request.data.get('project_ids', [])projects = Project.objects.filter(id__in=ids, created_by=request.user)updated = projects.update(is_archived=True)return Response({'archived_count': updated})
django
Breakdown
1
def get_permissions(self):
Override to return action-specific permission classes enabling fine-grained access control per endpoint
2
@action(detail=True, methods=['post'], permission_classes=[IsProjectAdmin])
Define custom action decorator with detail=True for instance-specific endpoint and explicit permission requirement
3
@action(detail=False, methods=['post'])
Create collection-level action for bulk operations without requiring a specific resource instance
4
IsProjectMember()
Use custom permission class that checks user's membership in the specific project being accessed
5
url_path='statistics'
Override URL path for the action to use 'statistics' instead of the method name 'stats'