目前,咱們的API對誰能夠編輯或刪除代碼段沒有任何限制。咱們想要一些更高級的行爲,以確保:html
代碼段始終與建立者相關聯。python
只有身份驗證的用戶能夠建立片斷。sql
只有片斷的建立者能夠更新或刪除它。數據庫
未經身份驗證的請求應具備徹底只讀訪問權限。django
咱們將對咱們的Snippet
模型類進行一些更改。首先,咱們添加幾個字段。其中一個字段將用於表示建立代碼段的用戶。另外一個字段將用於存儲代碼的突出顯示的HTML表示。編程
將如下兩個字段添加到Snippet
模型中models.py
。api
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()
在咱們的模型類中添加一個方法:ide
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'
字段,以及請求中驗證的數據。
如今,這些片斷與建立它們的用戶相關聯,咱們更新咱們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,)
若是您打開瀏覽器並導航到目前可瀏覽的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的權限,若是咱們要編輯任何片斷,咱們須要驗證咱們的請求。咱們尚未設置任何身份驗證類,因此默認值如今被應用,哪些是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中文文檔目錄: