Drf/Django开发记录

Drf官网教程(四) - 认证与许可

2018-06-23  本文已影响151人  dyq666

目录

  1. 修改model
  2. 增加User相关的代码
  3. 将user与snippet联系起来
  4. 增加认证的许可
  5. 自定义对象级的许可
  6. 关于Auth
  7. 总结

0. 概述

到目前为止,我们的Snippet提供了五种ACTION(LIST, RETRIEVE, DESTORY, UPDATE, CREATE)。但是我们还未提供用户这个概念,也没用为ACTION增加限制。因此规定下面四个规则:

  1. 每个snippet都关联一个用户(creator)
  2. 经过认证的用户可以使用LIST, RETRIEVE, CREATE
  3. 每个snippet只能被creator进行DESTORY, UPDATE
  4. 未经过认证的用户只能使用LIST, RETRIEVE

1. 修改model

...
class Snippet(models.Model):
    owner = models.ForeignKey('auth.User', related_name='snippets', on_delete=models.CASCADE)
    highlighted = models.TextField()
...

这部分只需要知道,我们可以overridemodels.Modelsave方法,来完成一些字段的修改或创建,比如这个例子中的字段highlighted就不是直接由用户传入的字段,而是通过用户传入的其他字段计算出来的。(举个其他的例子,在用户信息中常用到的字段:生日和年龄,我们只需要用户填入生日,然后在这个save方法中计算出年龄即可)最后调用父类的save方法

from pygments.lexers import get_lexer_by_name
from pygments.formatters.html import HtmlFormatter
from pygments import highlight

class Snippet(models.Model):
    ...
    def save(self, *args, **kwargs):
        """
        在save前先保存代码高亮字段
        """
        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)

由于前几章存储的record没有使用这两个字段,为了后面的调试我们采取的策略是:删除之前数据库相关的内容。

  1. 删除数据库(下面是linux的命令,win可以手动删除)
  2. makemigrations生成的snippets/migrations删除
  3. 重新生成数据库表
# win中需要手动删除
rm -f db.sqlite3
rm -r snippets/migrations
python manage.py makemigrations snippets
python manage.py migrate

python manage.py createsuperuser

2. 增加User相关的代码


这里Model使用的是Django内置的用户表。所以不需要第一步。

  1. django内置的用户表在django.contrib.auth.models.User
  2. 通过model中定义的related_name来反向获取snippets时,需要指定model集合,还因为是一对多的关系,所以还需要增加参数many=True
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')

user只提供LISTRETRIEVE两种action。CREATEaction目前只能通过python manage.py createsuperuser命令。

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

在前几章中由于snippet这个应用中只是用了一张表。所以我们将url的前缀snippet定义在drf_tutorial/urls.py

而现在user表和snippet表都在snippet应用中,所以先要修改一下drf_tutorial/urls.py

...
urlpatterns = [
    ...
    url(r'^', include('snippets.urls'))
]

snippets/urls中修改之前snippets的url,再增加user的url。

...
urlpatterns = format_suffix_patterns([
    url(r'^snippets/$', views.SnippetList.as_view()),
    url(r'^snippets/(?P<pk>[0-9]+)/$', views.SnippetDetail.as_view()),
    url(r'^users/$', views.UserList.as_view()),
    url(r'^usres/(?P<pk>[0-9]+)/$', views.UserDetail.as_view())
])

3. 将user与snippet联系起来

  1. 先看下mixins中create方法的源码

    源码
    在源码中可以看到,当调用验证完字段的方法(is_valid)后,调用了perform_create方法,这是Drf为我们提供的一个方便override的方法,让我们可以在验证后,对字段进行一定的处理。

    因此我们只需要重写这个perform_create方法即可。

  2. 重写perform_create
    views.SnippetList类中加入下面的方法。

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

在save方法中,可以传递一些命名参数来调整或增加字段的值(这里我们为snippet实例增加一个creator)。

在这里我们要增加creator,而当前请求的user是保存在self.request.user中的。

  1. 修改serializers.py
    为snippet的序列化类增加owner字段。
class SnippetSerializer(serializers.ModelSerializer):
    owner = serializers.ReadOnlyField(source='owner.username')
    ...
    class Meta:
        ...
        fields = (..., 'owner')
  1. 别忘了owner是外键!
  2. source参数代表了最终这个字段返回的值(这段代码中owner字段会返回用户名)
  3. ReadOnlyField在本例中等于CharField(read_only=True)
总结:如果某个model中使到用户作为外键,需要在对应CBV的view中重写perform_create方法,手动将外键指定为当前请求中的用户,然后再序列化中指定参数source来选择如何序列化这个外键。

4. 增加认证的许可

SnippetList & SnippetDetail加入下面的代码。

from rest_framework import permissions

permission_classes = (permissions.IsAuthenticatedOrReadOnly,)

drf_tutorial/urls.py

urlpatterns = [
    ...
    url(r'^drf-auth/', include('rest_framework.urls'))
]

drf-auth可以随意命名。(官网给的默认值是api-auth)

打开urlhttp://127.0.0.1:8000/snippets/,网页右上角会看到一个登录功能,登陆后在网页最下面可提交表单。

创建一个snippet

5. 自定义对象级的许可

完成每个snippet只能被creator进行DESTORY, UPDATE的功能。

from rest_framework import permissions


class IsSnippetOwnerOrReadOnly(permissions.BasePermission):
    """只有snipper的onwer才有权限资格进行删改更新"""
    
    def has_object_permission(self, request, view, obj):
        if request.method in permissions.SAFE_METHODS:
            return True
        
        # 当snipper的owner和当前认证的用户相同时才返回True
        return obj.owner == request.user
class SnippetDetail(generics.RetrieveUpdateDestroyAPIView):
    ...
    permission_classes = (permissions.IsAuthenticatedOrReadOnly,
                          IsSnippetOwnerOrReadOnly)

6. 关于Auth

在教程中没有指定authentication classes,所以使用的是默认的SessionAuthentication & BasicAuthentication

7. 总结

  1. Auth和Permission的区别
    Auth模块负责确定用哪种方式进行认证(例如本教程中使用的是Django默认的认证方式)。
    Permission负责管理方法的访问权。例如是否认证,是否满足实例的属性(对象级许可)。
  2. Permission类
  1. 提供了两种方法,在上文中其实我们是分别重写的这两种方法。
上一篇下一篇

猜你喜欢

热点阅读