1. 程式人生 > >Django REST FrameWork中文教程4:驗證和許可權

Django REST FrameWork中文教程4:驗證和許可權

目前,我們的API對誰可以編輯或刪除程式碼段沒有任何限制。我們想要一些更高階的行為,以確保:

  • 程式碼段始終與建立者相關聯。

  • 只有身份驗證的使用者可以建立片段。

  • 只有片段的建立者可以更新或刪除它。

  • 未經身份驗證的請求應具有完全只讀訪問許可權。

將資訊新增到我們的模型

我們將對我們的Snippet模型類進行一些更改。首先,我們新增幾個欄位。其中一個欄位將用於表示建立程式碼段的使用者。另一個欄位將用於儲存程式碼的突出顯示的HTML表示。

將以下兩個欄位新增到Snippet模型中models.py

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

我們還需要確保在儲存模型時,使用pygments程式碼突出顯示庫填充突出顯示的欄位。

我們需要一些額外的匯入:

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

現在我們可以.save()在我們的模型類中新增一個方法:

def save(self, *args, **kwargs):
    """
    Use the `pygments` library to create a highlighted HTML
    representation of the code snippet.
    """
    lexer = get_lexer_by_name(self.language)
    linenos = self.linenos and 'table' or False
    options = self.title and {'title': self.title} or {}
    formatter = HtmlFormatter(style=self.style, linenos=linenos,
                              full=True, **options)
    self.highlighted = highlight(self.code, lexer, formatter)
    super(Snippet, self).save(*args, **kwargs)

完成這些工作後,我們需要更新我們的資料庫表。通常我們將建立一個數據庫遷移,為了做到這一點,但是為了本教程的目的,我們只需刪除資料庫並重新開始

rm -f tmp.db db.sqlite3
rm -r snippets/migrations
python manage.py makemigrations snippets
python manage.py migrate

您可能還需要建立幾個不同的使用者,以用於測試API。執行此操作的最快方法是使用createsuperuser命令。

python manage.py createsuperuser

現在我們有一些使用者可以使用,我們最好將這些使用者的表示新增到我們的API中。建立一個新的serializer很容易。在serializers.py

新增:

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模型上'snippets'是一個反向關係,所以在使用ModelSerializer該類時它不會被預設包含,所以我們需要為它新增一個顯式欄位。

我們還會新增幾個檢視views.py。我們希望只使用只讀檢視為使用者表示,所以我們將使用ListAPIViewRetrieveAPIView通用的基於類的意見。

from django.contrib.auth.models import User


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


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

確保也匯入UserSerializer

from snippets.serializers import UserSerializer

最後,我們需要通過從URL conf引用它們將這些檢視新增到API中。將以下內容新增到其中的模式中urls.py

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

將片段與使用者關聯

現在,如果我們建立了一個程式碼片段,那麼將無法將建立該程式碼段的使用者與程式碼段例項相關聯。使用者不是作為序列化表示的一部分發送的,而是傳入請求的屬性。

我們處理的方式是覆蓋.perform_create()我們的程式碼片段檢視上的方法,這樣我們可以修改例項儲存的管理方式,並處理傳入請求或請求的URL中隱含的任何資訊。

SnippetList檢視類中,新增以下方法:

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

create()我們的序列器的方法現在將被傳遞一個附加'owner'欄位,以及請求中驗證的資料。

現在,這些片段與建立它們的使用者相關聯,我們更新我們SnippetSerializer來反映這一點。將以下欄位新增到序列化器定義中serializers.py

owner = serializers.ReadOnlyField(source='owner.username')

注意:確保您還新增'owner',到內部Meta類的欄位列表。

這個領域正在做一些很有趣的事情。的source哪個屬性引數控制用於填充的欄位,並且可以在對序列化例項的任何屬性點。它也可以採用上面顯示的點劃線,在這種情況下,它將以與Django模板語言一起使用的相似方式遍歷給定的屬性。

我們添加了欄位是型別化ReadOnlyField類,相對於其他型別的欄位,如CharFieldBooleanField等...型別化ReadOnlyField始終是隻讀的,並且將用於序列化表示形式,但不會被用於更新模型他們被反序列化的例項。我們也可以CharField(read_only=True)在這裡使用。

新增檢視所需的許可權

現在,程式碼片段與使用者相關聯,我們希望確保只有經過身份驗證的使用者才能建立,更新和刪除程式碼段。

REST框架包括許多許可權類,我們可以使用它們來限制誰可以訪問給定的檢視。在這種情況下,我們正在尋找的是IsAuthenticatedOrReadOnly,這將確保經過身份驗證的請求獲得讀寫訪問許可權,未經身份驗證的請求將獲得只讀訪問許可權。

首先在檢視模組中新增以下匯入

from rest_framework import permissions

接著,下面的屬性新增到SnippetListSnippetDetail檢視類。

permission_classes = (permissions.IsAuthenticatedOrReadOnly,)

如果您開啟瀏覽器並導航到目前可瀏覽的API,那麼您將發現無法再建立新的程式碼段。為了做到這一點,我們需要能夠以使用者身份登入。

我們可以通過編輯專案級urls.py檔案中的URLconf來新增可瀏覽API使用的登入檢視。

在檔案頂部新增以下匯入:

from django.conf.urls import include

並且,在檔案末尾,新增一個模式以包括可瀏覽的API的登入和登出檢視。

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

r'^api-auth/'模式的一部分實際上可以是您要使用的任何URL。唯一的限制是所包含的URL必須使用'rest_framework'名稱空間。在Django 1.9+中,REST框架將設定名稱空間,因此您可以將其刪除。

現在,如果再次開啟瀏覽器並重新整理頁面,您將在頁面右上角看到一個“登入”連結。如果您以您之前建立的使用者身份登入,則可以再次建立程式碼段。

建立幾個程式碼片段後,導航到“/ users /”端點,並注意到該表示包含每個使用者的“片段”欄位中與每個使用者相關聯的程式碼段的列表。

物件級許可權

我們希望所有的程式碼片段都可以被任何人看到,但也要確保只有建立程式碼段的使用者才能更新或刪除它。

要做到這一點,我們將需要建立一個自定義許可權。

在片段應用中,建立一個新檔案, permissions.py

from rest_framework import permissions


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

    def has_object_permission(self, request, view, obj):
        # Read permissions are allowed to any request,
        # so we'll always allow GET, HEAD or OPTIONS requests.
        if request.method in permissions.SAFE_METHODS:
            return True

        # Write permissions are only allowed to the owner of the snippet.
        return obj.owner == request.user

現在,我們可以通過編輯檢視類中的permission_classes屬性將該自定義許可權新增到我們的程式碼段例項端點SnippetDetail

permission_classes = (permissions.IsAuthenticatedOrReadOnly,
                      IsOwnerOrReadOnly,)

確保也匯入IsOwnerOrReadOnly類。

from snippets.permissions import IsOwnerOrReadOnly

現在,如果再次開啟瀏覽器,您會發現如果您以與建立程式碼段相同的使用者身份登入,“DELETE”和“PUT”操作只會顯示在程式碼段例項端點上。

使用API進行身份驗證

因為我們現在有一組API的許可權,如果我們要編輯任何片段,我們需要驗證我們的請求。我們還沒有設定任何身份驗證類,所以預設值現在被應用,哪些是SessionAuthenticationBasicAuthentication

當我們通過Web瀏覽器與API進行互動時,我們可以登入,然後瀏覽器會話將為請求提供所需的身份驗證。

如果我們以程式設計方式與API互動,我們需要在每個請求上顯式提供身份驗證憑據。

如果我們嘗試建立一個沒有驗證的程式碼段,我們會收到一個錯誤:

http POST http://127.0.0.1:8000/snippets/ code="print 123"

{
    "detail": "Authentication credentials were not provided."
}

我們可以通過包括我們之前建立的一個使用者的使用者名稱和密碼來成功提出請求。

http -a tom:password123 POST http://127.0.0.1:8000/snippets/ code="print 789"

{
    "id": 1,
    "owner": "tom",
    "title": "foo",
    "code": "print 789",
    "linenos": false,
    "language": "python",
    "style": "friendly"
}

概要

我們現在已經在我們的Web API上獲得了一個相當精細的許可權,併為系統的使用者和他們建立的程式碼段提供了終點。

在本教程的第5部分中,我們將介紹如何通過為突出顯示的片段建立一個HTML端點來將所有內容聯結在一起,並通過為系統中的關係使用超連結來提高API的凝聚力。

Django REST FrameWork中文文件目錄: