目前,我们的API对谁可以编辑或删除代码段没有任何限制。为了确保:
代码段总是与创建者相关联。
只有经过身份验证的用户才能创建代码段。
只有片段的创建者可以更新或删除它。
未经身份验证的请求应具有完全只读访问权限。
我们将对我们的Snippet模特课。首先,让我们添加几个字段。其中一个字段将用于表示创建代码段的用户。另一个字段将用于存储代码的突出显示HTML表示形式。
models.py
owner = models.ForeignKey('auth.User', related_name='snippets', on_delete=models.CASCADE)
highlighted = models.TextField()
我们还需要确保在保存模型时,使用pygments代码高亮库。
我们需要一些额外的import:
from pygments.lexers import get_lexer_by_name
from pygments.formatters.html import HtmlFormatter
from pygments import highlight
现在我们可以添加一个.save()方法到我们的模型类:
def save(self, *args, **kwargs):
"""
Use the `pygments` library to create a highlighted HTML
representation of the code snippet.
"""
lexer = get_lexer_by_name(self.language)
linenos = 'table' if self.linenos else False
options = {'title': self.title} if self.title else {}
formatter = HtmlFormatter(style=self.style, linenos=linenos,
full=True, **options)
self.highlighted = highlight(self.code, lexer, formatter)
super(Snippet, self).save(*args, **kwargs)
当这些完成后,我们将需要更新我们的数据库表。通常我们会创建一个数据库迁移来实现这一点,但是为了本教程的目的:更好理解整个过程的发生,我们需要删除数据库并重新开始。
rm -f db.sqlite3
rm -r snippets/migrations
python manage.py makemigrations snippets
python manage.py migrate
您还可能希望创建几个不同的用户,用于测试API。最快的方法是使用createsuperuser命令。
python manage.py createsuperuser
现在我们已经有一些用户要处理了,我们最好将这些用户的表示添加到我们的API中。创建一个新的序列化程序很容易。在……里面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']
因为’snippets’是倒转在用户模型上,默认情况下,当使用ModelSerializer类,因此我们需要为它添加一个显式字段。
from django.contrib.auth.models import User
class UserList(generics.ListAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
class UserDetail(generics.RetrieveAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
from snippets.serializers import UserSerializer
path('users/', views.UserList.as_view()),
path('users//' , views.UserDetail.as_view()),
现在,如果我们创建了一个代码片段,就没有办法将创建代码片段的用户与代码片段实例相关联。用户不是作为序列化表示的一部分发送,而是作为传入请求的属性发送。
我们处理这一问题的方法是重写.perform_create()方法,该方法允许我们修改实例保存的管理方式,并处理传入请求或请求URL中隐含的任何信息。
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
这个create()方法,我们的序列化程序现在将传递一个额外的’owner’字段以及来自请求的验证数据。
既然代码片段与创建它们的用户相关联,那么让我们更新我们的SnippetSerializer来反映这一点。将下列字段添加到serializers.py:
owner = serializers.ReadOnlyField(source='owner.username')
注:确保您还添加了’owner’,中的字段列表。Meta class。
这个领域正在做一些很有趣的事情。这个source参数控制用于填充字段的属性,并且可以指向序列化实例上的任何属性。它还可以采用上面所示的虚线符号,在这种情况下,它将以与Django的模板语言类似的方式遍历给定的属性。
我们添加的字段是未键入的ReadOnlyField类,与其他类型化字段相反,例如CharField, BooleanField等等.。非类型化ReadOnlyField总是只读的,并且将用于序列化表示,但在反序列化时不会用于更新模型实例。我们也可以用CharField(read_only=True)这里。
既然代码片段与用户相关联,我们希望确保只有经过身份验证的用户才能创建、更新和删除代码片段。
REST框架包括许多权限类,我们可以使用这些类来限制谁可以访问给定的视图。在这种情况下,我们要找的是IsAuthenticatedOrReadOnly,这将确保通过身份验证的请求获得读-写访问,而未经身份验证的请求获得只读访问。
首先在视图模块中添加以下导入
from rest_framework import permissions
然后,将以下属性添加到双管齐下这个SnippetList和SnippetDetail查看类。
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
如果您现在打开一个浏览器并导航到可浏览的API,您将发现您不再能够创建新的代码片段。为了做到这一点,我们需要能够以用户身份登录。
我们可以添加一个登录视图,以便与可浏览的api一起使用,方法是在我们的项目级别中编辑urlconf。urls.py档案。
在文件顶部添加以下导入:
from django.conf.urls import include
并且,在文件的末尾,添加一个模式来包含可浏览API的登录和注销视图。
urlpatterns += [
path('api-auth/', include('rest_framework.urls')),
]
这个’api-auth/'模式的一部分实际上可以是您想要使用的任何URL。
现在,如果您再次打开浏览器并刷新页面,您将在页面的右上角看到一个“Login”链接。如果您以前面创建的用户之一登录,您将能够再次创建代码段。
创建了几个代码段之后,导航到‘/user/’端点,并注意到表示在每个用户的“代码段”字段中包含了与每个用户关联的代码段ID的列表。
实际上,我们希望所有代码片段对任何人都是可见的,但也要确保只有创建代码片段的用户才能更新或删除它。
为此,我们需要创建一个自定义权限。
在片段应用程序中,创建一个新文件,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视图类:
permission_classes = [permissions.IsAuthenticatedOrReadOnly,
IsOwnerOrReadOnly]
也要确保导入IsOwnerOrReadOnly班级,等级。
from snippets.permissions import IsOwnerOrReadOnly
现在,如果您再次打开一个浏览器,您会发现‘DELETE’和‘PUT’操作仅出现在一个代码段实例端点上,如果您是作为创建代码片段的同一个用户登录的话。
因为我们现在对API拥有一组权限,所以如果我们想编辑任何片段,就需要对请求进行身份验证。我们还没有建立认证类,因此当前应用的默认值是:SessionAuthentication和BasicAuthentication.
当我们通过Web浏览器与API交互时,我们可以登录,然后浏览器会话将为请求提供所需的身份验证。
如果我们以编程方式与API交互,则需要显式地为每个请求提供身份验证凭据。
如果我们尝试在不进行身份验证的情况下创建一个代码片段,我们将得到一个错误:
http POST http://127.0.0.1:8000/snippets/ code="print(123)"
{
"detail": "Authentication credentials were not provided."
}
我们可以通过包含我们前面创建的一个用户的用户名和密码来发出成功的请求。
http -a admin:password123 POST http://127.0.0.1:8000/snippets/ code="print(789)"
{
"id": 1,
"owner": "admin",
"title": "foo",
"code": "print(789)",
"linenos": false,
"language": "python",
"style": "friendly"
}
摘要
现在,我们已经在我们的WebAPI上获得了一组颗粒度相等的权限(耦合度低),以及系统用户和他们创建的代码片段的端点。
在DjangoRestFramework系列教程(5):关系(关联)和超链接API在本教程中,我们将研究如何通过为突出显示的片段创建HTML端点来将所有内容联系在一起,并通过对系统中的关系使用超链接来提高API的凝聚力。