【DRF】基于 rest_auth token 认证用户作为 model 的 owner



前言

本文基于:

假设当前已有 DRF 的 model – snippet,并且安装运行好了 rest_auth 模块。

这篇博客目的是为 snippet 中的数据添加 owner;即从 SQL 数据库角度来说,为 snippet TABLE 的每一行数据都加上它们从属于哪一个用户的外键。

其次,在 LIST 和 DETAIL 都限定了只有 owner 才可以访问 snippet 中属于 owner 的数据。



1. 更新 model

1.1 添加外键

<django-proj>/snippet/models.py:

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




2. 更新 serializer

fields 添加 owner,以及对 owner.ReadOnlyField 限制。

<django-proj>/snippet/serializers.py

from rest_framework import serializers
from .models import Snippet


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

    class Meta:
        model = Snippet
        fields = ['owner',
                  'id', 'title', 'code',
                  'linenos', 'language', 'style']


3. 更新 view – LIST, DETAIL

因为 detail 部分相对容易,所以放在前面

3.1 Detail View

detail 的访问如:http://<domain>:<port>/api/snippet/3/

我们想要限制只在 pk==3 这条数据的 owner 是当前访问的的用户的时候,才允许当前用户查看、修改等,这样的实现如下:

[...]
from rest_framework import permissions
[...]

class IsOwner(permissions.BasePermission):
    """
    Custom permission to only allow owners of an object to edit it.
    """

    def has_object_permission(self, request, view, obj):
        # Write permissions are only allowed to the owner of the snippet.
        return obj.owner == request.user


[...]

class SnippetDetail(generics.RetrieveUpdateDestroyAPIView):
    permission_classes = (IsOwner, )  # add permission_classes
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer

为什么:N/A


3.2 List View

3.2.1 create at List View endpoint

在考虑”读取“当前用户的所有 model(Snippet) 数据之前,.../api/snippet/ 这个 endpoint 还承担着 Create 的功能(generics.ListCreateAPIView),本文要实现 create 一条数据的时候,owner (ForeignKey) 应当绑定到当前对应的用户上。
因此在 List View 中,我们先考虑和实现这一点:

class SnippetList(generics.ListCreateAPIView):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer

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

可以看到,我们只是为 SnippetList 重写了 perform_create 就能实现这一点 —— serializer.save 的时候绑定 owner 为当前请求的用户。这个用户(self.request.user)因为客户端通过 rest_authtoken (或着是登录过之后 Set 下去的 session_id)在 django 的中间件处理1过之后已经是 Python 代码中的 User2 实例,所以相当于已经对 Snippet model 中的 owner 赋了正确的值,而其它值在 POST 的 data 中,和修改前的行为一致。

TODO:关于 perform_create

3.2.2 retrieval of List View

List view 相对 Detail view 不太一样。
如果要限制用户只能看到当前属于他自己的数据,那么就意味着要 filter 出来 owner == 当前访问的用户;只是添加 permission_classes = (IsOwner, ) 不能达到目的。

为什么 permission_classes = (IsOwner, ) 行为不对?

正如上面所说的,对 List view 部分在 GET 的时候, 需要 filter 出来 own == <request.user>

class SnippetList(generics.ListCreateAPIView):
    permission_classes = (IsOwner, )  # just for notice, not acturlly work
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer

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

    def get_queryset(self):
        user = self.request.user
        return Snippet.objects.filter(owner=user)

如果是像 blog 这样的 model(应用),那么我们可能只是想限制 Detail View 的 not SAFE_METHODS3 操作,而 List View 的 Retrieval 不论从属,允许所有用户查看,那么只需要参照【DRF】Django REST Framework - Tutorial 4: 认证 和&权限 ? 简单添加 permission_classes = [permissions.IsAuthenticatedOrReadOnly] 即可。



4. 关于更新 model(添加外键)后 migrate 数据

  1. 删除数据库重建。
  2. 添加的外键要含有 defaultnull=True,然后做 migrate,最后再删除 null=True (和 default

Reference:



总结

结合 前言 的两个博客,一个 model 已经是完整的,可以上线使用的了。

  • DRF 的 generic 提供了简单实现 REST API 的能力。
  • rest_auth 提供了限制访问 model 的能力。
  • 本文提供了 model 内的数据和 owner (通过外键)绑定的能力,以及精细访问权限到用户级别的能力(原本是有无登录级别)。


Reference




  1. TODO: Django session 中间件/rest token 中间件相关行为。 ↩︎

  2. TODO: 更新准确的 Django 内置的 User model 描述。 ↩︎

  3. SAFE_METHODS: GET 是安全的(不会修改数据),POST、PUT、DELETE 不是安全的。 ↩︎

发布了164 篇原创文章 · 获赞 76 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/qq_29757283/article/details/98587698