实现 Django REST 框架教程中的自定义权限



我正在遵循 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.pySnippetDetail类中:

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')

这与序列化程序的定义/使用方式有关,这不是权限问题。字段"所有者"需要在序列化程序中设置为只读或不需要。

最新更新