到目前爲止,撰寫的API沒有任何限制關於誰能更新、刪除snippet. 咱們更想要一些高級行爲來確保:html
一、代碼段老是跟建立者有關聯python
二、只要認證經過的用戶才能建立sql
三、只有建立者有權限更新或者刪除數據庫
四、沒有認證的請求應該有且只有徹底的只讀權限django
咱們打算在Snippet模型類上作一些改變。首先,添加一些字段,其中之一用來表明建立這個code的用戶。其餘的字段將用於存儲代碼中突出顯示的HTML表示形式。api
添加下面兩個字段到Snippet類在models.py.瀏覽器
owner = models.ForeignKey('auth.User', related_name='snippets', on_delete=models.CASCADE) highlighted = models.TextField()
咱們還須要確保當模型被保存時,咱們使用pygments代碼突出顯示庫填充突出顯示的字段。此外還須要導入些其餘的:session
from pygments.lexers import get_lexer_by_name from pygments.formatters.html import HtmlFormatter from pygments import highlight
如今須要往model類裏面添加一個save方法:app
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 = 'table' if self.linenos else False options = {'title': self.title} if self.title else {} formatter = HtmlFormatter(style=self.style, linenos=linenos, full=True, **options) self.highlighted = highlight(self.code, lexer, formatter) super(Snippet, self).save(*args, **kwargs)
作好上面的操做後,須要同步數據庫測試
shuais-MacBook-Pro:TestApp dandyzhang$ rm -f db.sqlite3 shuais-MacBook-Pro:TestApp dandyzhang$ rm -r app01/migrations shuais-MacBook-Pro:TestApp dandyzhang$ python3 manage.py makemigrations shuais-MacBook-Pro:TestApp dandyzhang$ python3 manage.py migrate
爲了測試API,還須要建立一下超級用戶:
shuais-MacBook-Pro:TestApp dandyzhang$ python3 manage.py createsuperuser
如今咱們已經有一些工做的用戶了,還須要添加這些用戶的表示到API中。
在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')
snippets在 User model上是一種反轉關係,在繼承ModelSerializer類不能使用默認值。因此須要爲它添加一個明確的字段
一樣,也須要在views.py文件內添加一些東西。咱們更傾向於爲用戶表示使用只讀權限,因此咱們將使用ListAPIView和RetrieveAPIView generic CBV.
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 app01.serializers import UserSerializer
最後,咱們須要把這些視圖添加進API,經過url設置,在urls.py文件中
path('users/', views.UserList.as_view()), path('users/<int:pk>/', views.UserDetail.as_view()),
如今,若是咱們建立snippet,會發現沒有辦法經過snippet實例關聯到建立的用戶。用戶不是做爲序列化表示的一部分發送的,而是即將到來的請求的屬性。
解決這個問題的方式是重寫一個.perform_create()方法在views裏面,它容許咱們修改管理實例如何保存,處理傳入請求或者被請求URL的任何信息
在SnippetList視圖類添加以下的方法:
def perform_create(self, serializer): serializer.save(owner=self.request.user)
一個額外添加的owner字段如今將會被serializer裏面的create方法經過,跟請求裏面的驗證數據一塊兒。
如今snippets和用戶的關聯建好了,更新一下SnippetSerializer來反應它。在serializers.py文件添加:
owner = serializers.ReadOnlyField(source='owner.username')
注意將它添加在Meta類內部的字段列表中.
class SnippetSerializer(serializers.ModelSerializer): owner = serializers.ReadOnlyField(source='owner.username') class Meta: model = Snippet fields = ('id', 'title', 'code', 'linenos', 'language', 'style', 'owner')
這個字段作了些頗有趣的事情。source參數控制一種屬性用來跟某個字段關聯對應,能夠指向序列化實例的任何屬性。它也能夠採用上面所顯示的實例的表示法,將會遍歷給定的屬性,跟Django的模版語言很相似。
咱們剛剛添加的這個字段是一個非類型化的ReadOnlyField類,與其餘類型字段相反,好比CharField、BooleanField等...非類型話的ReadOnlyField老是隻讀的,並將用於序列化表示,可是在反序列化的時候不能被用來更新模型實例,也能夠用CharField(read_only=True)
如今snippet已經跟用戶有聯繫了,咱們想肯定只有經過驗證的用戶能夠建立,更新,刪除snippet。
REST framework包含了一串權限類供用來限制誰能訪問一個給定的視圖。在這裏,咱們想要尋找的是IsAuthenticatedOrReadOnly這個類用來確保經過驗證的請求獲取到讀寫權限,沒有經過驗證的請求得到只讀權限。
首先在views裏面導入模塊
from rest_framework import permissions
而後添加下面的屬性到SnippetList和SnippetDetail兩個視圖類中。
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
若是你打開瀏覽器,導航到了可瀏覽的API,你會發現你不能再建立新的spnippet了。爲了能這樣作,須要先登錄。
咱們能夠再根目錄的urls文件內加入下面的路由,它包含可瀏覽的API的登錄和登出。
path('api/', include('rest_framework.urls', namespace='rest_framework')),
路由的名稱能夠自定義。
如今打開瀏覽器,你會在網頁的右上角看到login的圖標。若是你用以前建立的用戶登錄了,你就能夠再一次建立snippet了。
建立好一些snippet後,你就能夠在子路由users下面查看到
咱們想讓全部的snippet被任何人看到,但必須保證只有建立的人才能夠增刪改。
想要這麼作,須要建立一個定製化的權限。建立一個新的文件在app內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
如今能夠添加自定義的權限到snippet實例終端,經過編輯在SnippetDetail視圖類裏面的permission_classes屬性
from app01.permissions import IsOwnerOrReadOnly # 先導入 permission_classes = (permissions.IsAuthenticatedOrReadOnly, IsOwnerOrReadOnly,)
如今從新運行一下此項目,你會發現delete和put按鈕出如今你建立的一個snippet實例的終端。
由於如今已經有一批權限在API上,若是須要編輯任何的snippet須要認證請求。可是咱們尚未設置任何的認證類,因此此時是使用的默認的類SessionAuthentication和BasicAuthentication
當咱們經過瀏覽器跟API進行交互,咱們能夠登錄,而後瀏覽器的session會提供必要的認證給請求。若是咱們以變成的方式跟API交互,就須要提供明確的認證憑據在每一次請求上。若是嘗試建立一個新的snippet不帶驗證,將會獲得報錯:
帶上認證憑據: