我正在遵循 Django REST 框架教程(第 4 部分(,但我观察到我的 API 的行为与本教程的预期之间存在差异。这是我的项目级urls.py
:
from django.contrib import admin
from django.urls import path, include
from rest_framework.urlpatterns import format_suffix_patterns
from snippets import views
urlpatterns = [
path('admin/', admin.site.urls),
path('users/', views.UserList.as_view()),
path('users/<int:pk>/', views.UserDetail.as_view()),
path('', include('snippets.urls'))
]
urlpatterns += [
path('api-auth/', include('rest_framework.urls'))
]
这是包含的snippets.urls
:
from django.urls import path
from rest_framework.urlpatterns import format_suffix_patterns
from snippets import views
urlpatterns = [
path('snippets/', views.SnippetList.as_view()),
path('snippets/<int:pk>/', views.SnippetDetail.as_view())
]
urlpatterns = format_suffix_patterns(urlpatterns)
我创建了一个具有自定义权限类permissions.py
IsOwnerOrReadOnly
:
from rest_framework import permissions
class IsOwnerOrReadOnly(permissions.BasePermission):
"""
Custom permission to only allow owners of an object to edit it.
"""
def has_object_permission(self, request, view, obj):
# Read permissions are allowed to any request,
# so we'll always allow GET, HEAD or OPTIONS requests.
if request.method in permissions.SAFE_METHODS:
return True
# Write permissions are only allowed to the owner of the snippet.
return obj.owner == request.user
我已经将这个类添加到permission_classes
views.py
的SnippetDetail
类中:
from django.contrib.auth.models import User
from snippets.models import Snippet
from snippets.serializers import SnippetSerializer, UserSerializer
from rest_framework import generics, permissions
from snippets.permissions import IsOwnerOrReadOnly
class UserList(generics.ListAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
class UserDetail(generics.RetrieveAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
class SnippetList(generics.ListCreateAPIView):
queryset = Snippet.objects.all()
serializer_class = SnippetSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
class SnippetDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Snippet.objects.all()
serializer_class = SnippetSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly, IsOwnerOrReadOnly,)
但是,当我发出仅指定我的凭据(使用 Httpie(的 POST 请求时,我收到一个 400 错误请求:
Kurts-MacBook-Pro:~ kurtpeek$ http -a kurtpeek:mypassword POST http://localhost:8000/snippets/ code="print 789"
HTTP/1.1 400 Bad Request
Allow: GET, POST, HEAD, OPTIONS
Content-Length: 37
Content-Type: application/json
Date: Wed, 24 Jan 2018 18:13:47 GMT
Server: WSGIServer/0.2 CPython/3.6.4
Vary: Accept, Cookie
X-Frame-Options: SAMEORIGIN
{
"owner": [
"This field is required."
]
}
显然,我需要指定owner="1"
才能使其工作:
Kurts-MacBook-Pro:~ kurtpeek$ http -a kurtpeek:mypassword POST http://localhost:8000/snippets/ code="print 789" owner="1"
HTTP/1.1 201 Created
Allow: GET, POST, HEAD, OPTIONS
Content-Length: 103
Content-Type: application/json
Date: Wed, 24 Jan 2018 18:13:35 GMT
Server: WSGIServer/0.2 CPython/3.6.4
Vary: Accept, Cookie
X-Frame-Options: SAMEORIGIN
{
"code": "print 789",
"id": 3,
"language": "python",
"linenos": false,
"owner": 1,
"style": "friendly",
"title": ""
}
但是,正如我从IsOwnerOrReadOnly
权限类的has_object_permission
方法的逻辑中了解到的那样,所有者应该从request.user
中推断出来。这也不是本教程中给出的示例的方式。谁能发现这里出了什么问题?
POST 请求应转到您的SnippetList
视图,而不是SnippetDetail
,因此不使用自定义权限类。
不过,我不知道为什么会出现此错误。根据教程,您应该在SnippetSerializer
中将owner
设置为只读字段。
owner = serializers.ReadOnlyField(source='owner.username')
这与序列化程序的定义/使用方式有关,这不是权限问题。字段"所有者"需要在序列化程序中设置为只读或不需要。