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

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 天凈沙 · 化緣 山高路遠天朗, 石疏水清氣爽, 想是神筆仙降, 洋洋灑灑, 荷魚研墨染塘。 不知道其他人讀了...
    eleis閱讀 230評論 1 1
  • 前端攻擊成因 在web網頁的腳本中,有些部分的顯示內容會依據外界輸入值而發生變化,而如果這些聲稱html的程序中存...
    隨波逐流007閱讀 882評論 0 1
  • 利用Photoshop鼠繪兩只水墨蝦,主要用到的工具:鋼筆、加深、減淡、濾鏡等;繪制之前先分解一下作品,然后分段用...
    運和閱讀 722評論 0 2