目前,我們的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
。我們希望只使用只讀視圖為用戶表示,所以我們將使用ListAPIView
和RetrieveAPIView
通用的基於類的意見。
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'
字段,以及請求中驗證的數據。
更新我們的serializer
現在,這些片段與創建它們的用戶相關聯,我們更新我們SnippetSerializer
來反映這一點。將以下字段添加到序列化器定義中serializers.py
:
owner = serializers.ReadOnlyField(source='owner.username')
注意:確保您還添加'owner',
到內部Meta
類的字段列表。
這個領域正在做一些很有趣的事情。的source
哪個屬性參數控制用於填充的字段,並且可以在對串行化實例的任何屬性點。它也可以采用上面顯示的點划線,在這種情況下,它將以與Django模板語言一起使用的相似方式遍歷給定的屬性。
我們添加了字段是類型化ReadOnlyField
類,相對於其他類型的字段,如CharField
,BooleanField
等...類型化ReadOnlyField
始終是只讀的,並且將用於序列化表示形式,但不會被用於更新模型他們被反序列化的實例。我們也可以CharField(read_only=True)
在這里使用。
添加視圖所需的權限
現在,代碼片段與用戶相關聯,我們希望確保只有經過身份驗證的用戶才能創建,更新和刪除代碼段。
REST框架包括許多權限類,我們可以使用它們來限制誰可以訪問給定的視圖。在這種情況下,我們正在尋找的是IsAuthenticatedOrReadOnly
,這將確保經過身份驗證的請求獲得讀寫訪問權限,未經身份驗證的請求將獲得只讀訪問權限。
首先在視圖模塊中添加以下導入
from rest_framework import permissions
接着,下面的屬性添加到都在SnippetList
和SnippetDetail
視圖類。
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
添加登錄到Browsable API
如果您打開瀏覽器並導航到目前可瀏覽的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的權限,如果我們要編輯任何片段,我們需要驗證我們的請求。我們還沒有設置任何身份驗證類,所以默認值現在被應用,哪些是SessionAuthentication
和BasicAuthentication
。
當我們通過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中文文檔目錄: