目前,我们的API对谁可以编辑或删除代码段没有任何限制。我们希望有一些更高级的行为,以确保:
将对我们的Snippet
模型类进行一些更改。首先,让我们添加几个字段。其中一个字段将用于表示创建代码段的用户。另一个字段将用于存储代码的突出显示的HTML表示。
owner = models.ForeignKey('auth.User', related_name='snippets', on_delete=models.CASCADE)
highlighted = models.TextField()
我们还需要确保在保存模型时,使用pygments
代码突出显示库填充突出显示的字段
from pygments.lexers import get_lexer_by_name
from pygments.formatters.html import HtmlFormatter
from pygments import highlight
完成所有操作后,我们需要更新数据库表。通常我们会创建一个数据库迁移来执行此操作,但是出于本教程的目的,我们只需删除数据库并重新开始。
# 删除原先的数据库和数据迁移记录
# Linux:
rm -f db.sqlite3
rm -r snippets/migrations
# window:
del db.sqlite3
rd /s/q snippets\migrations
# 数据库生成河迁移
python manage.py makemigrations snippets
python manage.py migrate
# 您可能还想创建一些不同的用户,以用于测试API。最快的方法是使用createsuperuser命令。
python manage.py createsuperuser
# serializers.py
from django.contrib.auth.models import User
class UserSerializer(serializers.ModelSerializer):
snippets = serializers.PrimaryKeyRelatedField(many=True, queryset=Snippet.objects.all())
class Meta:
model = User
fields = ('id', 'username', 'snippets')
from django.contrib.auth.models import User
from snippets.serializers import UserSerializer
class UserList(generics.ListAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
class UserDetail(generics.RetrieveAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
# Django version >=2.0
path('users/', views.UserList.as_view()),
path('users//', views.UserDetail.as_view()),
# Django version <2.0
from django.conf.urls import include,url
url(r'^users/$', views.UserList.as_view()),
url(r'^users/(?P[0-9]+)/$', views.UserDetail.as_view()),
截至目前,我们有代码段到用户的模型,序列化器,用户的序列化器,能够实现查询时候的相互查询。
但是当我们新建代码时,还是无法实现用户与代码段的互相关联的。
原因是:新建一个代码片段时候,根据已经完成的model(保含有owner),View;但我们需要的用户信息不是想代码code那样在在请求提交的form表单里面的,而是在request.user里。我们还需要对View进行修改。
我们通过的.perform_create()
方法是通过覆盖我们的代码段视图上的方法,它允许我们修改实例保存的管理方式,并处理传入请求或请求的URL中隐含的任何信息。
class SnippetList(generics.ListCreateAPIView):
queryset = Snippet.objects.all()
serializer_class = SnippetSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
def perform_create(self, serializer):
# 保存之前按,加入owner信息
# 通过覆盖我们的代码段视图上的方法,允许我们修改实例保存的管理方式,并处理传入请求或请求的URL中隐含的任何信息。
serializer.save(owner=self.request.user)
class SnippetSerializer(serializers.ModelSerializer):
owner = serializers.ReadOnlyField(source='owner.username')
# source 用于填充的字段,连接其他表的任何字段
# ReadOnlyField 是只读字段,能用于序列化,但不作为更新model使用,效果等价于CharField(read_only=True)
class Meta:
model = Snippet
fields = ('id', 'title', 'code', 'linenos', 'language', 'style','owner')
既然代码片段与用户相关联,我们希望确保只有经过身份验证的用户才能创建,更新和删除代码段。
REST框架包含许多权限类,我们可以使用这些权限来限制谁可以访问给定视图。在这种情况下,我们正在寻找的是IsAuthenticatedOrReadOnly
,这将确保经过身份验证的请求获得读写访问权限,未经身份验证的请求将获得只读访问权限。
# snippets\views.py
from rest_framework import permissions
# 在SnippetList和SnippetDetail视图类加入
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
如果您此时打开浏览器并导航到可浏览的API,您将发现您无法再创建新的代码段。为此,我们需要能够以用户身份登录。
我们可以通过编辑项目级urls.py
文件中的URLconf来添加用于可浏览API的登录视图。
修改如下:
from django.conf.urls import include
# if Django version >=2.0
。。。
urlpatterns += [
path('api-auth/', include('rest_framework.urls')),
]
。。。
# else:
urlpatterns += [
path('api-auth/', include('rest_framework.urls')),
]
打开浏览器访问
实际上,我们希望所有人都可以看到所有代码段,但也要确保只有创建代码段的用户才能更新或删除它。
为此,我们需要创建自定义权限。
在代码段应用中,创建一个新文件, permissions.py
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
属性将该自定义权限添加到我们的代码段实例端点SnippetDetail
:
from snippets.permissions import IsOwnerOrReadOnly
permission_classes = (permissions.IsAuthenticatedOrReadOnly,
IsOwnerOrReadOnly,)
不进行身份验证的情况下创建代码段,则会收到错误消息
加了认证后,可以正常请求,换回201Created状态码,对象成功创建。
总结:
对接口加权限是出于网站安全和业务需要的,主要实现依赖于permission,自定义权限继承permissions.BasePermission重构自己需要的业务权限,结果返波尔值。
# 如有理解错误,请指出 #