Django REST framework 第四章 Authentication
到目前為止,撰寫的API沒有任何限制關於誰能更新、刪除snippet. 我們更想要一些高級行為來確保:
1、代碼段總是跟創建者有關聯
2、只要認證通過的用戶才能創建
3、只有創建者有權限更新或者刪除
4、沒有認證的請求應該有且只有完全的只讀權限
Adding information to our model
我們打算在Snippet模型類上做一些改變。首先,添加一些字段,其中之一用來代表創建這個code的用戶。其他的字段將用於存儲代碼中突出顯示的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
現在需要往model類裏面添加一個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 = ‘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
Adding endpoints for our User models
現在我們已經有一些工作的用戶了,還需要添加這些用戶的表示到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()),
Associating Snippets with Users
現在,如果我們創建snippet,會發現沒有辦法通過snippet實例關聯到創建的用戶。用戶不是作為序列化表示的一部分發送的,而是即將到來的請求的屬性。
解決這個問題的方式是重寫一個.perform_create()方法在views裏面,它允許我們修改管理實例如何保存,處理傳入請求或者被請求URL的任何信息
在SnippetList視圖類添加如下的方法:
def perform_create(self, serializer): serializer.save(owner=self.request.user)
一個額外添加的owner字段現在將會被serializer裏面的create方法通過,跟請求裏面的驗證數據一起。
Updating our serializer
現在snippets和用戶的關聯建好了,更新一下SnippetSerializer來反應它。在serializers.py文件添加:
owner = serializers.ReadOnlyField(source=‘owner.username‘)
註意將它添加在Meta類的內部.
這個字段做了些很有趣的事情。source參數控制一種屬性用來跟某個字段關聯對應,可以指向序列化實例的任何屬性。它也可以采用上面所顯示的實例的表示法,將會遍歷給定的屬性,跟Django的模版語言很類似。
我們剛剛添加的這個字段是一個非類型化的ReadOnlyField類,與其他類型字段相反,比如CharField、BooleanField等...非類型話的ReadOnlyField總是只讀的,並將用於序列化表示,但是在反序列化的時候不能被用來更新模型實例,也可以用CharField(read_only=True)
Adding required permissions to views
現在snippet已經跟用戶有聯系了,我們想確定只有通過驗證的用戶可以創建,更新,刪除snippet。
REST framework包含了一串權限類供用來限制誰能訪問一個給定的視圖。在這裏,我們想要尋找的是IsAuthenticatedOrReadOnly這個類用來確保通過驗證的請求獲取到讀寫權限,沒有通過驗證的請求獲得只讀權限。
首先在views裏面導入模塊
from rest_framework import permissions
然後添加下面的屬性到SnippetList和SnippetDetail兩個視圖類中。
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
Adding login to Browable API
如果你打開瀏覽器,導航到了可瀏覽的API,你會發現你不能再創建新的spnippet了。為了能這樣做,需要先登陸。
我們可以再根目錄的urls文件內加入下面的路由,它包含可瀏覽的API的登陸和登出。
path(‘api/‘, include(‘rest_framework.urls‘, namespace=‘rest_framework‘)),
路由的名稱可以自定義。
現在打開瀏覽器,你會在網頁的右上角看到login的圖標。如果你用之前創建的用戶登陸了,你就可以再一次創建snippet了。
創建好一些snippet後,你就可以在子路由users下面查看到
Object level permissions
我們想讓所有的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實例的終端。
Authenticating with the API
因為現在已經有一批權限在API上,如果需要編輯任何的snippet需要認證請求。但是我們還沒有設置任何的認證類,所以此時是使用的默認的類SessionAuthentication和BasicAuthentication
當我們通過瀏覽器跟API進行交互,我們可以登陸,然後瀏覽器的session會提供必要的認證給請求。如果我們以變成的方式跟API交互,就需要提供明確的認證憑據在每一次請求上。如果嘗試創建一個新的snippet不帶驗證,將會得到報錯:
帶上認證憑據:
Django REST framework 第四章 Authentication