Django cookbook · Intermediate
Restrict API objects to their owners in DRF
Users can read or change other users' records by guessing IDs.
Approach
Filter the queryset by the current user (so other users' objects are 404) and add an object-level permission
for write rules. Set the owner from request.user on create; never accept it from the request body.
Solution
from rest_framework import permissions, serializers, viewsets
from notes.models import Note
class NoteSerializer(serializers.ModelSerializer):
class Meta:
model = Note
fields = ["id", "title", "body", "shared", "created_at"]
read_only_fields = ["created_at"]
class IsOwnerOrReadOnlyShared(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
if request.method in permissions.SAFE_METHODS:
return obj.owner_id == request.user.id or obj.shared
return obj.owner_id == request.user.id
class NoteViewSet(viewsets.ModelViewSet):
serializer_class = NoteSerializer
permission_classes = [permissions.IsAuthenticated, IsOwnerOrReadOnlyShared]
def get_queryset(self):
user = self.request.user
return Note.objects.filter(owner=user) | Note.objects.filter(shared=True)
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
Pitfalls
- Only checking permissions in
has_permission; object rules needhas_object_permission(andget_object()). - Including
owneras a writable serializer field. - Overriding
list()with a custom queryset that bypassesget_queryset()filtering.
Variations
- Return 404 instead of 403 for other users' objects by filtering the queryset (no information leak).
- For roles and teams, use
django-guardianor explicit membership tables.
Further reading: DRF permissions
Reference: https://www.django-rest-framework.org/api-guide/permissions/
More Django recipes
- Add a required column without downtime
- Add full-text search with PostgreSQL
- Add health checks and structured logging
- Avoid N+1 queries with select_related and prefetch_related
- Cache expensive queries and invalidate on change
- Call external APIs concurrently in async views
- Configure settings from environment variables
- Enforce rules with database constraints