Django Rest 框架 - 当用户不是对象所有者时,拒绝来自 PUSH 的用户



目前,我已经设置了权限,如果用户不是Stock的对象所有者,则可以阻止用户进行GET,DELETE和PUT。但是由于某种原因,当用户执行 PUSH 时,权限不起作用,即任何用户都可以将Note推送到Stock,即使他们不是Stock Owner

为什么?我如何正确检查当User推送Note时,他们必须是Stock的所有者?


这是通过HTTPie发送的示例数据推送:

http -a testuser:testpw POST http://127.0.0.1:8000/api/v1/notes/ note="Testing API" stock="36"

其中"36"是现有股票的stock_id。

这是stock_note/models.py

from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
import uuid
class Stock(models.Model):
'''
Model representing the stock info.
'''
user = models.ForeignKey(User)
book_code = models.CharField(max_length=14, null=True, blank=True)
def __str__(self):
return self.book_code
class Note(models.Model):
'''
Model representing the stock note.
'''
user = models.ForeignKey(User)
note = models.TextField(max_length=560)
stock = models.ForeignKey(Stock, related_name='notes')
date_note_created = models.DateTimeField(default=timezone.now)
def __str__(self):
return self.note

这是api/serializers.py

from stock_note.models import Stock, Note
from rest_framework import serializers
class StockSerializer(serializers.ModelSerializer):
user = serializers.HiddenField(default=serializers.CurrentUserDefault())
notes = serializers.PrimaryKeyRelatedField(read_only=True, many=True)
class Meta:
model = Stock
fields = ('id', 'user', 'book_code', 'notes')
class NoteSerializer(serializers.ModelSerializer):
user = serializers.HiddenField(default=serializers.CurrentUserDefault())
class Meta:
model = Note
fields = ('user', 'note', 'stock')

这是api/views.py

from rest_framework import generics
from stock_note.models import Stock, Note
from api.serializers import StockSerializer, NoteSerializer
from rest_framework.permissions import IsAuthenticated
from api.permissions import IsOwner
# Create your views here.
class StockList(generics.ListCreateAPIView):
serializer_class = StockSerializer
permission_classes = (IsAuthenticated, IsOwner)
def get_queryset(self):
user = self.request.user
return Stock.objects.filter(user=user)
def perform_create(self, serializer):
serializer.save()
def perform_update(self, serializer):
serializer.save()
class NoteList(generics.ListCreateAPIView):
serializer_class = NoteSerializer
permission_classes = (IsAuthenticated, IsOwner)
def get_queryset(self):
user = self.request.user
return Note.objects.filter(user=user)
def perform_create(self, serializer):
serializer.save()
def perform_update(self, serializer):
serializer.save()
class StockListDetail(generics.RetrieveUpdateDestroyAPIView):
serializer_class = StockSerializer
permission_classes = (IsAuthenticated, IsOwner)
lookup_url_kwarg = 'stock_id'
def get_queryset(self):
stock = self.kwargs['stock_id']
return Stock.objects.filter(id=stock)
class NoteListDetail(generics.RetrieveUpdateDestroyAPIView):
serializer_class = NoteSerializer
permission_classes = (IsAuthenticated, IsOwner)
lookup_url_kwarg = 'note_id'
def get_queryset(self):
note = self.kwargs['note_id']
return Note.objects.filter(id=note)

这是api/permissions.py

from rest_framework import permissions
class IsOwner(permissions.BasePermission):
def has_permission(self, request, view):
return request.user and request.user.is_authenticated()
def has_object_permission(self, request, view, obj):
return obj.user == request.user

最后是api/urls.py

from django.conf.urls import url, include
from api import views
urlpatterns = [
#Endpoint to allow GET and POST stocks.
url(r'^v1/stocks/$', views.StockList.as_view()),
#Endpoint to allow GET and POST a note to a stock.
url(r'^v1/notes/$', views.NoteList.as_view()),
#Endpoint to allow GET, POST, PUSH, DELETE a stocknote
url(r'^v1/stocks/(?P<stock_id>[0-9]+)/$', views.StockListDetail.as_view()),
#Endpoint to allow GET, POST, PUSH, DELETE a Note
url(r'^v1/notes/(?P<note_id>[0-9]+)/$', views.NoteListDetail.as_view()),
]

更新:

继Tom的回答之后,NoteSerializer现在看起来像这样,这意味着用户现在只能推送Notes,如果他们是股票的所有者(新添加的是validate_stock功能)。请注意,Tom 的答案和这段代码之间有一个区别:我不只是检查value,而是检查value.id。这在validate_stock函数的注释中进一步解释:

class NoteSerializer(serializers.ModelSerializer):
user = serializers.HiddenField(default=serializers.CurrentUserDefault())
class Meta:
model = Note
fields = ('user', 'note', 'stock')
def validate_stock(self, value):
'''
This function checks if the User is the owner of Stock
before allowing the User to PUSH a Note to the Stock.
'''
# You have to get the object ID because otherwise you get following error when
# you try to perform Stock.object.get(...):
#TypeError: int() argument must be a string, a bytes-like object or a number, not 'Stock'
value_id = value.id
stock_obj = Stock.objects.get(pk=value_id)
user = self.context['request'].user
if not stock_obj.user == user:
raise serializers.ValidationError("You do not have permission to perform this action.")
return value

当您POSTv1/notes/时,将运行的唯一权限检查是has_permission。URL 中没有引用现有实例,因此不会在视图上调用get_object,也不会调用has_object_permission检查(没有实例可以调用它)。

在这种情况下,您需要对序列化程序类强制实施验证,以确保股票值必须与用户拥有的 Stock 实例相对应。

类似的东西...

def validate_stock(self, value):
stock = Stock.objects.get(pk=value)
user = self.context['request'].user
if not stock.user == user:
raise serializers.ValidationError(...)
return value

最新更新