Django_REST Framework -5.验证与授权

验证与授权

目前来看,我们的 API 并没有权限上的限制(即任何人都可以编辑或删除我们的 Movies ),这不是我们想要的。所以我们需要在 API 上做些限制以确保:

  • Movies 与 Users 关联起来。
  • 只有授权了的用户才能创建新的 Movies。
  • 只有 Movies 的创建者才可以更新或删除它。
  • 未授权的用户只能进行查看。

在 models 中增加以下信息

我们先把之前注释掉的

director = models.ForeignKey('celebrity', related_name='Movies')

class celebrity(models.Model):
    name = models.CharField(max_length=100, blank=True, default='')
    age = models.IntegerField()
    gender = models.CharField(choices=GENDER_CHOICES, default='male', max_length=20)

关联导演类的注释解开,来看看多张表在生成的 api 里的关联性。

接着在 models.py 中的 Movies 类中加入以下代码来确定 Movies 的创建者:

owner = models.ForeignKey('auth.User', related_name='Movies')

最后 models.py 代码为:

#!/usr/bin/python
# -*- coding: utf-8 -*-

from django.db import models

# 举个栗子
COUNTRY_CHOICES = (
    ('US', 'US'),
    ('Asia', 'Asia'),
    ('CN', 'CN'),
    ('TW', 'TW'),
)
TYPE_CHOICES = (
    ('Drama', 'Drama'),
    ('Thriller', 'Thriller'),
    ('Sci-Fi', 'Sci-Fi'),
    ('Romance', 'Romance'),
    ('Comedy', 'Comedy')
)
GENDER_CHOICES = (
    ('male', 'male'),
    ('female', 'female')
)

class Movies(models.Model):
    title = models.CharField(max_length=100, blank=True, default='')
    year = models.CharField(max_length=20)
    # 在 director 关联了 Movies 类 和 celecrity 类, 在第4章会用到 celebrity 类
    director = models.ForeignKey('celebrity', related_name='movies')
    # 关联 User 类来确定 Movies 的创建者
    owner = models.ForeignKey('auth.User', related_name='movies')
    country = models.CharField(choices=COUNTRY_CHOICES, default='US', max_length=20)
    type = models.CharField(choices=TYPE_CHOICES, default='Romance', max_length=20)
    rating = models.DecimalField(max_digits=3, decimal_places=1)
    created = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ('created',)

class celebrity(models.Model):
    name = models.CharField(max_length=100, blank=True, default='')
    age = models.IntegerField()
    gender = models.CharField(choices=GENDER_CHOICES, default='male', max_length=20)

修改完了模型,我们需要更新一下数据表。

通常来讲,我们会创建一个数据库 migration 来更新数据表,但是为了图省事儿,宝宝我索性删了整张 Movies 表直接重建!

在数据库中删除 douban_movies 表后在终端中执行以下命令:

$ python manage.py syncdb

接着我们可能会需要多个 User 来测试 API ,如果之前你没有创建 Django Super User 的话,用以下命令创建:

$ python manage.py createsuperuser

然后进入 http://127.0.0.1/admin/ 界面,登录并找到 /user/ 表,然后在里面手动创建 user 并赋予权限。

为新增的模型增加 endpoints

既然现在我们已经有了 users 模型和 celebrity 模型,那么现在需要做的就是在 serializer.py 中让他们在 API 中展现出来,加入以下代码:

class UserSerializer(serializers.ModelSerializer):
    movies = serializers.PrimaryKeyRelatedField(many=True, queryset=Movies.objects.all())

    class Meta:
        model = User
        fields = ('id', 'username', 'movies')

class DirectorSerializer(serializers.ModelSerializer):
    movies = serializers.PrimaryKeyRelatedField(many=True, queryset=Movies.objects.all())

    class Meta:
        model = celebrity
        fields = ('id', 'name', 'age', 'gender', 'movies')

因为我们之前在 models.py 中添加了 owner = models.ForeignKey('auth.User', related_name='movies') 其中 related_name 设置了可以通过 User.movies 来逆向访问到 movies 表。所以在 ModelSerializer 类中我们需要在 fields 中添加一个 movies 来实现逆向访问。同理 DirectorSerializer 类中也进行相应修改。

接着,我们还需要在 views.py 中添加相应的视图。

为 User 添加只读 API ,使用 ListAPIViewRetrieveAPIView

为 Director 添加读写 API ,使用 ListCreateAPIViewRetrieveUpdateDestroyAPIView

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

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

class DirectorList(generics.ListCreateAPIView):
    queryset = celebrity.objects.all()
    serializer_class = DirectorSerializer

class DirectorDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = celebrity.objects.all()
    serializer_class = DirectorSerializer

最后,修改 urls.py 把视图关联起来,在 urlpatterns 中加入以下4个 patterns:

urlpatterns = [
    url(r'^users/$', views.UserList.as_view()),
    url(r'^users/(?P<pk>[0-9]+)/$', views.UserDetail.as_view()),
    url(r'^directors/$', views.DirectorList.as_view()),
    url(r'^directors/(?P<pk>[0-9]+)/$', views.DirectorDetail.as_view()),
]

把 Movies 和 Director 、 User 关联起来

现在,如果我们新建一部 movie ,那它和 director 还有 user 是没有关联的,因为 director 和 user 信息是通过 request 接收到的,而不是通过序列器接收的,这意味着,数据库中收到 director 和 user 信息是没有(和 movies 存在)外键关系的。

而要让他们发生关系 ,我们的做法是在视图中重写 .perform_create() 方法。

.perform_create() 方法允许我们处理 request 或 requested URL 中的任何信息。

MoviesListMoviesDetail 中添加以下代码:

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

这样 create() 方法就能够在接收到 request.data 时将其传回给序列器里的 owner 和 director 了。

更新序列器

在视图中重写了 .perform_create() 方法后还需要更新下序列器才能实现他们之间的关联,在 serializer.py 中的 MoviesSerializer 类添加以下代码:

owner = serializers.ReadOnlyField(source='owner.username')
director = serializers.CharField(source='celebrity.name')

接着在 class Meta 的 fields 中加入 owner 和 director :

class Meta:
    model = Movies
    fields = ('id', 'title', 'director', 'year', 'country', 'type', 'rating', 'owner')

source 关键字负责控制在 fields 中展现的数据的源,它可以指向这个序列器实例的任意一个属性。

对 owner 属性,我们用的是 ReadOnlyField 在确保它始终是只读的,我们也可以用 CharField(read_only=True) 来等效替代,但是我嫌它太长了,其余的 Field 还有诸如 CharFieldBooleanField 等,你可以在 「这里」查到。

添加权限

我们希望授权的用户才能新建、更新和删除 movies,所以需要添加权限管理的功能。

DRF 包含了一系列的 permission 类来实现权限管理,你可以在「这里」 查到。

在这个栗子中,我们使用 IsAuthenticatedOrReadOnly 来确保授权的请求得到读写的权限,未授权的请求只有只读权限。

首先,在 views.py 中 import 以下模块:

from rest_framework import permissions

接着,在 MoviesListMoviesDetail 中加入以下代码:

permission_classes = (permissions.IsAuthenticatedOrReadOnly,)

添加可浏览的授权 api

如果你在浏览器中访问我们的 api Web 界面,你会发现我们没法创建新的 movies 了,因为在上一步我们设置了权限管理。

所以我需要在浏览器中添加用户登录来实现带界面的权限管理。(之所以说带界面是因为可以在终端中直接使用 httpie 来访问 api )

restapi/urls.py 中加入以下代码:

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

这样通过在浏览器中访问 Web api 界面就能在右上角发现一个登录按钮,进行登录授权了。

对象级权限

之前提到要使 movies 可以被任何人访问,但是只能被创建者编辑,所以需要赋予其游客访问的权限以及创建者编辑权限。

下面我们新建一个 permissions.py 来详细解决这个权限问题:

#!/usr/bin/python
# -*- coding: utf-8 -*-
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

修改 views.pyMoviesDetailpermission_class :

from douban.permissions import IsOwnerOrReadOnly

permission_classes = (permissions.IsAuthenticatedOrReadOnly,
                      IsOwnerOrReadOnly,)

终于,我们完成了整个 api 授权的过程!

https://github.com/thehackercat/django-rest-framework-tutorial/blob

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容

  • 天净沙 · 化缘 山高路远天朗, 石疏水清气爽, 想是神笔仙降, 洋洋洒洒, 荷鱼研墨染塘。 不知道其他人读了...
    eleis阅读 207评论 1 1
  • 前端攻击成因 在web网页的脚本中,有些部分的显示内容会依据外界输入值而发生变化,而如果这些声称html的程序中存...
    随波逐流007阅读 854评论 0 1
  • 利用Photoshop鼠绘两只水墨虾,主要用到的工具:钢笔、加深、减淡、滤镜等;绘制之前先分解一下作品,然后分段用...
    运和阅读 671评论 0 2