通过上一个章节中的示例代码,应该不难发现目前API操作并没有任何限制。我们希望有一些更高级的行为来确保:

  • snippets始终与创建者相关联。
  • 只有进过身份验证的用户才可以创建snippets。
  • 只有snippets的创建者才可以更新和删除它。
  • 为经身份验证的请求应具有完全只读访问权限。

创建models.py文件

from django.db import models
from pygments.lexers import get_all_lexers, get_lexer_by_name
from pygments.styles import get_all_styles
from pygments.formatters.html import HtmlFormatter
from pygments import highlight


# 获取pygments支持的所有语言的此法分析程序
LEXERS = [item for item in get_all_lexers() if item[1]]
# 获取pygments支持的所有语言列表
LANGUAGE_CHOICES = sorted([(item[1][0], item[0]) for item in LEXERS])
# 获取pygments支持的所有格式化风格列表
STYLE_CHOICES = sorted((item, item) for item in get_all_styles())


class Snippet(models.Model):
    created = models.DateTimeField(auto_now_add=True)
    title = models.CharField(max_length=100, blank=True, default="")
    code = models.TextField()
    linenos = models.BooleanField(default=False)
    language = models.CharField(choices=LANGUAGE_CHOICES, default="python", max_length=100)
    style = models.CharField(choices=STYLE_CHOICES, default="friendly", max_length=100)
    owner = models.ForeignKey("auth.User", related_name="snippets", on_delete=models.CASCADE)
    highlighted = models.TextField()

    class Meta:
        ordering = ("created",)

    def save(self, *args, **kwargs):
        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)

serializers.py

如果有一些用户可以使用,最好将这些用户的表示添加到API中。

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在用户模型中是反向关系,当使用ModelSerilalizer类时,它将不包含默认值,所以需要为它添加一个显示字段。

views.py

这次使用LsitAPIViewRetrieveAPIView通用的基于类的视图来完成新图示的编写。

from django.contrib.auth.models import User
from rest_framework import generics
from snippets.models import Snippet
from snippets.serializers import SnippetSerializer, UserSerializer
from snippets.permissions import IsOwnerOrReadOnly


class SnippetList(generics.ListAPIView, generics.CreateAPIView):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer


class SnippetDetail(generics.RetrieveAPIView, generics.UpdateAPIView):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer


class UserList(generics.ListAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer


class UserDetail(generics.RetrieveAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer

上面的实例代码中,是无法将创建的snippet的用户与snippet实例关联起来。用户不是按座位序列化的一部分发送的,而是做为传入请求的属性。
我们需要在snippet视图上覆盖.perform_create()方式,这允许我们修改实例保存的管理方式,并处理传入请求或请求的URL中隐含的任何信息。
在SnippetList视图中,添加下面的方式:

def perform_create(self, serializer):
    serializer.save(owner=self.request.user)

更新序列化器

目前snippets与创建它们的用户相关联了,此时需要更新SnippetSerializer体现这一点。

owner = serializers.ReadOnlyField(source='owner.username')

注意:确保owner是添加到class Meta的字段列表中。
完整示例:

class SnippetSerializer(serializers.ModelSerializer):
    owner = serializers.ReadOnlyField(source='owner.username')

    class Meta:
        model = Snippet
        fields = ("id", "title", "code", "linenos", "language", "style", "owner")

添加视图所需的权限

现在snippets和用户相关,此时,我们希望确定只有经过身份验证的用户才能创建、更细和删除snippets。
REST framework包含许多权限类,我们可以用来限制谁可以访问给定的视图。在这种情况下,我们需要的是IsAuthenticatedOrReadOnly类。它将确保经过身份验证的请求获得读写访问权限,未经过身份验证的请求将获得只读访问权限。
首先需要在视图函数中导入from rest_framework import permissions
然后在SnippetList和SnippetDetail视图类中添加一下属性:
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)

urls.py配置

如果现在打开浏览器并导航到可浏览的API,将会发现无法再创建新的snippet。为了做到这一点,我们需要能够以用户身份登录。
在tutorial目录下的urls.py中的URLconf来添加可浏览API的登录视图。
示例:

from django.conf.urls import include

urlpatterns = [
    ...
    path('api-auth/', include('rest_framework.urls', namespace='rest_framework')),
]

此时再重启服务,并刷新页面就会发现右上角多了一个登录的链接,如果我们已经创建了用户的话,那么就可以直接登录后,进行再次创建snippet。

对象级权限

实际上,我们希望所有人可以看到所有的snippets,但也要确保只有创建snippet的用户才能更新和删除它。为此,我们需要创建一个自定义权限。
在snippets应用中,新建一个permission.py文件。

from rest_framework import permissions


class IsOwnerOrReadOnly(permissions.BasePermission):
    def has_object_permission(self, request, view, obj):
        if request.method in permissions.SAFE_METHODS:
            return True
        return obj.owner == request.user

此时,我们就可以通过编辑SnippetDetail视同中permission_classes属性来将自定义添加到我们的snippet实例端点。并且需要确保我们已经导入我们编写的自定义权限类。

from snippets.permissions import IsOwnerOrReadOnly

class SnippetDetail(generics.RetrieveAPIView, generics.UpdateAPIView):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer
    permission_classes = (permissions.IsAuthenticatedOrReadOnly, IsOwnerOrReadOnly,)
0条评论

相关推荐

django教程

r

Django 2019-05-20 10:53:53

Celery

celery学习资料

Django 2019-05-25 18:41:55

django教程入门

适合django新人使用的学习手册

Django 2019-07-01 14:28:04

django实用资料

django项目从0到1自己总结的实用的资料,大部分常用的功能这里都有

Django 2019-05-08 18:21:34