目前,我设置了权限,如果用户不是对象所有者,则可以阻止他们进行 GET、DELETE 和 PUT 操作Stock
。但由于某种原因,当用户执行 PUSH 时,权限不起作用,即任何用户都可以 PUSHNote
to a Stock
即使他们不是Stock Owner
.
为什么?我该如何正确检查User
推 aNote
,他们必须是所有者Stock
?
这是通过以下方式发送的数据 PUSH 示例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()),
]
UPDATE:
根据 Tom 的回答,NoteSerializer 现在看起来像这样,这意味着用户现在只能在是 Stock 所有者的情况下推送注释(新添加的是 validate_stock 函数)。请注意,汤姆的答案和这段代码之间有一个区别:而不是仅仅检查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