Django REST Framework用户权限问题:仅允许访问自身创建内容
问题描述
我正在用REST API开发Web应用,现有4个用户(其中3个是农民用户),想要实现权限控制,让每个用户仅能查看和删除自己添加到后端的内容,但目前没能成功实现。以下是我的代码:
models.py
User = get_user_model() # Create your models here. class Tender(models.Model): id = models.AutoField(primary_key=True) name = models.CharField(max_length=255) author = models.ForeignKey(User, on_delete=models.CASCADE) description = models.TextField() date_due = models.DateField(default=datetime.date.today) location = models.CharField(max_length=255, null=False) contact = models.PositiveIntegerField(null=False) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) def __str__(self): return self.name class Input(models.Model): id = models.AutoField(primary_key=True) name = models.CharField(max_length=255) author = models.ForeignKey(User, on_delete=models.CASCADE) description = models.TextField() price = models.DecimalField(max_digits=10, decimal_places=2) quantity = models.DecimalField(max_digits=10, decimal_places=2) contact = models.PositiveIntegerField(null=False) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) def __str__(self): return self.name class Investor(models.Model): id = models.AutoField(primary_key=True) name = models.CharField(max_length=255, unique=True) author = models.ForeignKey(User, on_delete=models.CASCADE) description = models.TextField() location = models.CharField(max_length=255, null=False) contact = models.PositiveIntegerField(null=False) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) def __str__(self): return self.name
permissions.py
from rest_framework import permissions class IsOwnerOrReadOnly(permissions.BasePermission): def has_object_permission(self, request, view, obj): if request.method in permissions.SAFE_METHODS: return True if request.user == obj.author: return True return False class IsOwnerOnly(permissions.BasePermission): def has_object_permission(self, request, view, obj): if request.user == obj.user : return True return False
views.py
from requests import request from rest_framework.viewsets import ModelViewSet from rest_framework import viewsets, status, permissions from django.http import Http404, JsonResponse from rest_framework.views import Response from .models import Tender, Input , Investor from .serializers import TenderViewSerializer, InputViewSerializer, InvestViewSerializer from user.models import User from user.permissions import IsOwnerOnly, IsOwnerOrReadOnly # Create your views here. class TenderViewSet(viewsets.ModelViewSet): serializer_class = TenderViewSerializer permission_classes = (permissions.IsAuthenticated,) def get_queryset(self): if self.request.user.is_tender_holder: queryset = Tender.objects.all() return queryset else: return Response({"NO_ACCESS": "Access Denied"}, status=401) def create(self, request, *args, **kwargs): if self.request.user.is_tender_holder: new_tender = Tender.objects.create( name=request.data["name"], description=request.data["description"], date_due=request.data["date_due"], location=request.data["location"], contact=request.data['contact'] ) new_tender.save() serializer = TenderViewSerializer(new_tender) return Response(serializer.data) else: return Response({"NO_ACCESS": "Access Denied"}, status=401) def destroy(self, request, *args, **kwargs): if self.request.user.is_tender_holder: try: instance =self.get_object() self.perform_destroy(instance) except Http404: pass return Response({"message": "Tender deleted successfully"}) else: return Response({"NO_ACCESS": "Access Denied"}, status=401) # def update(self, request, *args, **kwargs): class InvestorViewSet(ModelViewSet): serializer_class = InvestViewSerializer permission_classes = (permissions.IsAuthenticated,) #queryset = Investor.objects.all() def get_queryset(self): if self.request.user.is_investor: queryset = Investor.objects.all() return queryset else: return Response({"NO_ACCESS": "Access Denied"}, status=401) def create(self, request, *args, **kwargs): if self.request.user.is_investor: new_investment = Investor.objects.create( name=request.data["name"], description=request.data["description"], location=request.data["location"], contact=request.data['contact'] ) new_investment.save() serializer = InvestViewSerializer(new_investment) return Response(serializer.data) else: return Response({"NO_ACCESS": "Access Denied"}, status=401) def destroy(self, request, *args, **kwargs): if self.request.user.is_investor: try: instance =self.get_object() self.perform_destroy(instance) except Http404: pass return Response({"message": "Investment deleted successfully"}) else: return Response({"NO_ACCESS": "Access Denied"}, status=401) class InputViewSet(ModelViewSet): serializer_class = InputViewSerializer permission_classes = (permissions.IsAuthenticated, IsOwnerOrReadOnly) #queryset = Input.objects.all() def get_queryset(self, **kwargs): if self.request.user.is_input_holder or self.request.user.is_superuser: user = self.request.user return Input.objects.filter(id=user.id) else: return Response({"NO_ACCESS": "Access Denied"}, status=401) def create(self, request, *args, **kwargs): if self.request.user.is_input_holder or self.request.user.is_superuser: new_input = Input.objects.create( name=request.data["name"], description=request.data["description"], price=request.data["price"], quantity=request.data["quantity"], contact=request.data['contact'] ) new_input.save() serializer = InputViewSerializer(new_input) return Response(serializer.data) else: return Response({"NO_ACCESS": "Access Denied"}, status=401) def destroy(self, request, *args, **kwargs): if self.request.user.is_input_holder or self.request.user.is_superuser: try: instance =self.get_object() self.perform_destroy(instance) except Http404: pass return Response({"message": "Input deleted successfully"}) else: return Response({"NO_ACCESS": "Access Denied"}, status=401)
问题排查与修正方案
核心问题点
get_queryset返回类型错误:该方法要求返回QuerySet对象,但你在else分支返回了Response,会直接抛出异常,正确做法是返回空QuerySet或抛出权限异常。- 创建内容未关联作者:创建Tender、Investor、Input时,没有将当前登录用户赋值给
author字段,导致后续无法通过author判断内容归属。 - 权限类未正确应用:TenderViewSet和InvestorViewSet未添加
IsOwnerOrReadOnly,导致同类型用户可以操作所有内容。 - Input查询条件错误:
Input.objects.filter(id=user.id)是用Input的ID匹配用户ID,逻辑完全错误,应该用author=request.user过滤用户自己的内容。 - 销毁方法跳过权限校验:自定义的destroy方法没有校验用户是否为内容作者,导致同类型用户可删除他人内容。
- IsOwnerOnly类字段错误:模型中关联用户的字段是
author,但代码里写的是obj.user,字段名不匹配。
修正后的代码
permissions.py
from rest_framework import permissions class IsOwnerOrReadOnly(permissions.BasePermission): def has_object_permission(self, request, view, obj): # 安全方法仅允许查看自己的内容 if request.method in permissions.SAFE_METHODS: return request.user == obj.author # 写入/删除仅允许作者本人 return request.user == obj.author class IsOwnerOnly(permissions.BasePermission): def has_object_permission(self, request, view, obj): return request.user == obj.author
views.py
from rest_framework.viewsets import ModelViewSet from rest_framework import permissions, status from rest_framework.response import Response from .models import Tender, Input, Investor from .serializers import TenderViewSerializer, InputViewSerializer, InvestViewSerializer from user.permissions import IsOwnerOrReadOnly class TenderViewSet(ModelViewSet): serializer_class = TenderViewSerializer permission_classes = (permissions.IsAuthenticated, IsOwnerOrReadOnly) def get_queryset(self): # 仅返回当前用户创建的Tender return Tender.objects.filter(author=self.request.user) def create(self, request, *args, **kwargs): if not request.user.is_tender_holder: return Response({"NO_ACCESS": "Access Denied"}, status=status.HTTP_401_UNAUTHORIZED) # 关联当前用户为作者 request.data['author'] = request.user.id return super().create(request, *args, **kwargs) class InvestorViewSet(ModelViewSet): serializer_class = InvestViewSerializer permission_classes = (permissions.IsAuthenticated, IsOwnerOrReadOnly) def get_queryset(self): # 仅返回当前用户创建的Investor return Investor.objects.filter(author=self.request.user) def create(self, request, *args, **kwargs): if not request.user.is_investor: return Response({"NO_ACCESS": "Access Denied"}, status=status.HTTP_401_UNAUTHORIZED) request.data['author'] = request.user.id return super().create(request, *args, **kwargs) class InputViewSet(ModelViewSet): serializer_class = InputViewSerializer permission_classes = (permissions.IsAuthenticated, IsOwnerOrReadOnly) def get_queryset(self): # 仅返回当前用户创建的Input return Input.objects.filter(author=self.request.user) def create(self, request, *args, **kwargs): if not (request.user.is_input_holder or request.user.is_superuser): return Response({"NO_ACCESS": "Access Denied"}, status=status.HTTP_401_UNAUTHORIZED) request.data['author'] = request.user.id return super().create(request, *args, **kwargs)
关键优化说明
- 简化create逻辑:不再手动创建对象,通过父类
super().create()处理,同时将当前用户ID传入请求数据,让Serializer自动关联作者。 - 精准过滤查询集:所有ViewSet的
get_queryset仅返回当前用户创建的内容,确保用户只能看到自己的数据。 - 权限类全局生效:所有需要权限控制的ViewSet都添加
IsOwnerOrReadOnly,确保只有作者能修改/删除内容。 - 移除自定义destroy:ModelViewSet默认的destroy方法会自动调用权限类校验,无需自定义,避免跳过权限检查。
内容的提问来源于stack exchange,提问作者Tush
相关产品推荐
相关产品推荐

