Django cookbook · Intermediate

Restrict API objects to their owners in DRF

Users can read or change other users' records by guessing IDs.

Create a free account to save this page, take notes and track your progress. Sign in

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 need has_object_permission (and get_object()).
  • Including owner as a writable serializer field.
  • Overriding list() with a custom queryset that bypasses get_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-guardian or explicit membership tables.

Further reading: DRF permissions

Reference: https://www.django-rest-framework.org/api-guide/permissions/

More Django recipes

Was this page helpful?