Django_REST Framework -5.驗(yàn)證與授權(quán)

驗(yàn)證與授權(quán)

目前來(lái)看铡恕,我們的 API 并沒(méi)有權(quán)限上的限制(即任何人都可以編輯或刪除我們的 Movies )材蹬,這不是我們想要的嗤形。所以我們需要在 API 上做些限制以確保:

  • Movies 與 Users 關(guān)聯(lián)起來(lái)无埃。
  • 只有授權(quán)了的用戶才能創(chuàng)建新的 Movies鹤树。
  • 只有 Movies 的創(chuàng)建者才可以更新或刪除它赴背。
  • 未授權(quán)的用戶只能進(jìn)行查看椰拒。

在 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)

關(guān)聯(lián)導(dǎo)演類的注釋解開(kāi),來(lái)看看多張表在生成的 api 里的關(guān)聯(lián)性凰荚。

接著在 models.py 中的 Movies 類中加入以下代碼來(lái)確定 Movies 的創(chuàng)建者:

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

最后 models.py 代碼為:

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

from django.db import models

# 舉個(gè)栗子
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 關(guān)聯(lián)了 Movies 類 和 celecrity 類, 在第4章會(huì)用到 celebrity 類
    director = models.ForeignKey('celebrity', related_name='movies')
    # 關(guān)聯(lián) User 類來(lái)確定 Movies 的創(chuàng)建者
    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)

修改完了模型燃观,我們需要更新一下數(shù)據(jù)表。

通常來(lái)講便瑟,我們會(huì)創(chuàng)建一個(gè)數(shù)據(jù)庫(kù) migration 來(lái)更新數(shù)據(jù)表缆毁,但是為了圖省事兒,寶寶我索性刪了整張 Movies 表直接重建到涂!

在數(shù)據(jù)庫(kù)中刪除 douban_movies 表后在終端中執(zhí)行以下命令:

$ python manage.py syncdb

接著我們可能會(huì)需要多個(gè) User 來(lái)測(cè)試 API 脊框,如果之前你沒(méi)有創(chuàng)建 Django Super User 的話,用以下命令創(chuàng)建:

$ python manage.py createsuperuser

然后進(jìn)入 http://127.0.0.1/admin/ 界面践啄,登錄并找到 /user/ 表浇雹,然后在里面手動(dòng)創(chuàng)建 user 并賦予權(quán)限。

為新增的模型增加 endpoints

既然現(xiàn)在我們已經(jīng)有了 users 模型和 celebrity 模型屿讽,那么現(xiàn)在需要做的就是在 serializer.py 中讓他們?cè)?API 中展現(xiàn)出來(lái)昭灵,加入以下代碼:

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')

因?yàn)槲覀冎霸?models.py 中添加了 owner = models.ForeignKey('auth.User', related_name='movies') 其中 related_name 設(shè)置了可以通過(guò) User.movies 來(lái)逆向訪問(wèn)到 movies 表。所以在 ModelSerializer 類中我們需要在 fields 中添加一個(gè) movies 來(lái)實(shí)現(xiàn)逆向訪問(wèn)。同理 DirectorSerializer 類中也進(jìn)行相應(yīng)修改虎锚。

接著硫痰,我們還需要在 views.py 中添加相應(yīng)的視圖。

為 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 把視圖關(guān)聯(lián)起來(lái)柱徙,在 urlpatterns 中加入以下4個(gè) 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 關(guān)聯(lián)起來(lái)

現(xiàn)在,如果我們新建一部 movie 护侮,那它和 director 還有 user 是沒(méi)有關(guān)聯(lián)的敌完,因?yàn)?director 和 user 信息是通過(guò) request 接收到的,而不是通過(guò)序列器接收的羊初,這意味著滨溉,數(shù)據(jù)庫(kù)中收到 director 和 user 信息是沒(méi)有(和 movies 存在)外鍵關(guān)系的。

而要讓他們發(fā)生關(guān)系 长赞,我們的做法是在視圖中重寫 .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 時(shí)將其傳回給序列器里的 owner 和 director 了得哆。

更新序列器

在視圖中重寫了 .perform_create() 方法后還需要更新下序列器才能實(shí)現(xiàn)他們之間的關(guān)聯(lián)脯颜,在 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 關(guān)鍵字負(fù)責(zé)控制在 fields 中展現(xiàn)的數(shù)據(jù)的源,它可以指向這個(gè)序列器實(shí)例的任意一個(gè)屬性贩据。

對(duì) owner 屬性栋操,我們用的是 ReadOnlyField 在確保它始終是只讀的,我們也可以用 CharField(read_only=True) 來(lái)等效替代饱亮,但是我嫌它太長(zhǎng)了矾芙,其余的 Field 還有諸如 CharFieldBooleanField 等近尚,你可以在 「這里」查到蠕啄。

添加權(quán)限

我們希望授權(quán)的用戶才能新建场勤、更新和刪除 movies戈锻,所以需要添加權(quán)限管理的功能。

DRF 包含了一系列的 permission 類來(lái)實(shí)現(xiàn)權(quán)限管理和媳,你可以在「這里」 查到格遭。

在這個(gè)栗子中,我們使用 IsAuthenticatedOrReadOnly 來(lái)確保授權(quán)的請(qǐng)求得到讀寫的權(quán)限留瞳,未授權(quán)的請(qǐng)求只有只讀權(quán)限拒迅。

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

from rest_framework import permissions

接著,在 MoviesListMoviesDetail 中加入以下代碼:

permission_classes = (permissions.IsAuthenticatedOrReadOnly,)

添加可瀏覽的授權(quán) api

如果你在瀏覽器中訪問(wèn)我們的 api Web 界面璧微,你會(huì)發(fā)現(xiàn)我們沒(méi)法創(chuàng)建新的 movies 了作箍,因?yàn)樵谏弦徊轿覀冊(cè)O(shè)置了權(quán)限管理。

所以我需要在瀏覽器中添加用戶登錄來(lái)實(shí)現(xiàn)帶界面的權(quán)限管理前硫。(之所以說(shuō)帶界面是因?yàn)榭梢栽诮K端中直接使用 httpie 來(lái)訪問(wèn) api )

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

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

這樣通過(guò)在瀏覽器中訪問(wèn) Web api 界面就能在右上角發(fā)現(xiàn)一個(gè)登錄按鈕胞得,進(jìn)行登錄授權(quán)了。

對(duì)象級(jí)權(quán)限

之前提到要使 movies 可以被任何人訪問(wèn)屹电,但是只能被創(chuàng)建者編輯阶剑,所以需要賦予其游客訪問(wèn)的權(quán)限以及創(chuàng)建者編輯權(quán)限。

下面我們新建一個(gè) permissions.py 來(lái)詳細(xì)解決這個(gè)權(quán)限問(wèn)題:

#!/usr/bin/python
# -*- coding: utf-8 -*-
from rest_framework import permissions


class IsOwnerOrReadOnly(permissions.BasePermission):
    """
    游客訪問(wèn)權(quán)限及創(chuàng)建者編輯權(quán)限
    """

    def has_object_permission(self, request, view, obj):
        # 游客權(quán)限
        if request.method in permissions.SAFE_METHODS:
            return True

        # 編輯權(quán)限
        return obj.owner == request.user

修改 views.pyMoviesDetailpermission_class :

from douban.permissions import IsOwnerOrReadOnly

permission_classes = (permissions.IsAuthenticatedOrReadOnly,
                      IsOwnerOrReadOnly,)

終于危号,我們完成了整個(gè) api 授權(quán)的過(guò)程牧愁!

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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市外莲,隨后出現(xiàn)的幾起案子猪半,更是在濱河造成了極大的恐慌,老刑警劉巖偷线,帶你破解...
    沈念sama閱讀 219,427評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件办龄,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡淋昭,警方通過(guò)查閱死者的電腦和手機(jī)俐填,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)翔忽,“玉大人英融,你說(shuō)我怎么就攤上這事⌒剑” “怎么了驶悟?”我有些...
    開(kāi)封第一講書人閱讀 165,747評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)材失。 經(jīng)常有香客問(wèn)我痕鳍,道長(zhǎng),這世上最難降的妖魔是什么龙巨? 我笑而不...
    開(kāi)封第一講書人閱讀 58,939評(píng)論 1 295
  • 正文 為了忘掉前任笼呆,我火速辦了婚禮,結(jié)果婚禮上旨别,老公的妹妹穿的比我還像新娘诗赌。我一直安慰自己,他們只是感情好秸弛,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,955評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布铭若。 她就那樣靜靜地躺著洪碳,像睡著了一般。 火紅的嫁衣襯著肌膚如雪叼屠。 梳的紋絲不亂的頭發(fā)上瞳腌,一...
    開(kāi)封第一講書人閱讀 51,737評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音镜雨,去河邊找鬼纯趋。 笑死,一個(gè)胖子當(dāng)著我的面吹牛冷离,可吹牛的內(nèi)容都是我干的吵冒。 我是一名探鬼主播,決...
    沈念sama閱讀 40,448評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼西剥,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼痹栖!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起瞭空,我...
    開(kāi)封第一講書人閱讀 39,352評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤揪阿,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后咆畏,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體南捂,經(jīng)...
    沈念sama閱讀 45,834評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,992評(píng)論 3 338
  • 正文 我和宋清朗相戀三年旧找,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了溺健。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,133評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡钮蛛,死狀恐怖鞭缭,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情魏颓,我是刑警寧澤岭辣,帶...
    沈念sama閱讀 35,815評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站甸饱,受9級(jí)特大地震影響沦童,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜叹话,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,477評(píng)論 3 331
  • 文/蒙蒙 一偷遗、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧渣刷,春花似錦鹦肿、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,022評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至碌嘀,卻和暖如春涣旨,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背股冗。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,147評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工霹陡, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人止状。 一個(gè)月前我還...
    沈念sama閱讀 48,398評(píng)論 3 373
  • 正文 我出身青樓烹棉,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親怯疤。 傳聞我的和親對(duì)象是個(gè)殘疾皇子浆洗,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,077評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 天凈沙 · 化緣 山高路遠(yuǎn)天朗, 石疏水清氣爽集峦, 想是神筆仙降伏社, 洋洋灑灑, 荷魚研墨染塘塔淤。 不知道其他人讀了...
    eleis閱讀 215評(píng)論 1 1
  • 前端攻擊成因 在web網(wǎng)頁(yè)的腳本中摘昌,有些部分的顯示內(nèi)容會(huì)依據(jù)外界輸入值而發(fā)生變化,而如果這些聲稱html的程序中存...
    隨波逐流007閱讀 864評(píng)論 0 1
  • 利用Photoshop鼠繪兩只水墨蝦高蜂,主要用到的工具:鋼筆聪黎、加深、減淡备恤、濾鏡等挺举;繪制之前先分解一下作品,然后分段用...
    運(yùn)和閱讀 701評(píng)論 0 2