django rest framework我的學習筆記(六)————Tutorial4.認證於受權

官網地址
目前,咱們的API沒有對如 edit 或者 delect作出任何限制。咱們但願有一些更加高級的功能可以作到:html

  • Code snippets 應該永遠和建立者相關
  • 只有認證的用戶纔可以建立snippets
  • 只有建立者才能更新或者刪除他的snippet
  • 沒有認真的用戶應該只能讀

向咱們的model添加內容

咱們對咱們的code snippets models作一些修改。首先,咱們添加幾個字段。其中一個字段用於表示code snippet的建立者。另一個字段用於儲存突出顯示的代碼的HTML表示形式。
添加下列兩個字段到 models.py文件中的Snippetmodel。python

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

咱們也須要確保,當這個model被保存的時候,咱們使用pygments代碼高亮庫來填充咱們的highlighted字段。
咱們須要導入一些東西:sql

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

而後咱們須要爲咱們的Snippetmodel添加.save()方法數據庫

def save(self,*args,**kwargs):
    """
    使用'pygments'庫來建立高亮HTML代碼的表示
    """
    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)

當全部都昨晚以後,咱們須要更新咱們的數據庫。一般,爲了實現這一點,咱們會建立數據遷移文件(migration),可是本教程用於教學,因此咱們直接刪除數據庫並從新建立。django

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

你也能夠建立幾個不一樣的用戶來測試咱們的API。最快的方式就是使用createsuperuser命令。編程

python manage.py createsuperuser

爲咱們的User models 添加終端

如今,咱們有一些可用的用戶了。咱們最好爲咱們的API添加用戶代理。api

from django.contrib.auth.models import User

class UserSerializer(serializer.ModelSerializer):
    snippets = serializer.PrimaryKeyRelatedField(many=True,queryset=Snippet.objects.all())
    
    class Meta:
        model = User
        fields = {"id","username","snippets"}

由於snippetsUsermodels 的反向關係,在默認的ModelSerializerclass中,並不會包含這種關係,因此咱們須要添加一個明確的字段來表示這個關係。瀏覽器

咱們還須要添加一些view在views.py文件中。咱們但願咱們的用戶表示爲只讀視圖。因此咱們使用ListAPIViewRetrieveAPIView通用class-based views.session

from django.contrib.auth.models import User
from snippets.Serializers import UserSerializer

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

最後,咱們須要添加咱們的視圖到API中,添加如下的patterns到urls.py文件中:app

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

關聯User和Snippets

如今,若是咱們建立一個code snippet,咱們沒法將建立的snippet和建立者關聯起來,User數據不是做爲序列化的一部分發送的,而是做爲request的一個屬性.咱們處理這個問題的方法是,在咱們的snippet views中重寫.perform_create()方法,這容許咱們修改實例保存的方式,處理任何傳入請求和請求url中的參數。
SnippetListview class中,添加以下方法:

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

咱們的create()序列化方法將會從request中驗證經過的數據裏添加額外的owner字段。

更新咱們的serializer

如今,咱們的snippet已經和他的建立者關聯了。讓咱們來更新SnippetSerializer來體現這個關聯。添加如下的字段在咱們的serializers.py文件中:

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

注意:確保你的Meta class的fields中添加了'owner'
這個字段作了一個頗有趣的事。這個source參數控制哪個屬性用於填充字段,而且能夠指向這個序列化實例上的任何屬性,它也可使用.來訪問屬性下的屬性,這相似於django的模板語言.
這個字段咱們也可以添加ReadOnlyFieldclass,來不一樣於其餘字段,如CharField,BooleanField等...ReadOnlyField永遠只能讀,而且使用序列化表示,可是當咱們反序列化時,不能更新模塊。咱們也可以使用CharField(read_only=True)

爲咱們的視圖添加必要的權限

如今,咱們的code snippets 已經和users關聯,咱們但願確保只有認證的用戶纔可以建立,更新和刪除code snippets。
REST framework包含了幾種permission class,咱們可使用這些類來限制哪些人有權限使用這些view.在這個案例中,咱們須要的是IsAuthenticateOrReadOnly。這將確保認證用戶擁有讀寫權限,而未認證用戶只有讀的權限。
首先,添加如下的內容到views模塊。

from rest_framework import permissions

而後,添加如下的屬性到SnippetListSnippetDetailview classes

permission_classes = (permissions.IsAuthenticatedOrReadOnly,)

在咱們的Browsable API中添加登陸

若是你打開瀏覽器並導航到browsable API,你發現你沒有登陸,不可以建立新的code snippets.爲了作到這一點,咱們須要用戶可以登陸。
咱們可以在咱們的browsable API中添加login view,來編寫咱們的項目級urls.py文件(根urls.py文件)。
添加如下內容到文件中:

from django.conf.urls import include

而後,添加login和logout view在咱們的browsable API.

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

事實上,r'^api-auth/'部分,你可使用任何你想用的url。惟一的限制是namespace必須是rest_framework。在django1.9+中,REST framework將設置namespace,因此你能夠把這個限制排除在外。
如今,若是你再一次打開瀏覽器並刷新頁面,你將會看到頁面的右上角看到登陸連接。
建立了一些code snippets後,導航到'users/',在每一個user的snippets字段中,都會顯示相對應的code snippets。

Object級別的權限

咱們但願全部用戶都能看到全部code snippets,可是隻有建立者可以修改或者刪除他的code snippets.
爲了實現這個功能,咱們須要建立一個自定義的permission.
在snippets app 中,建立一個新文件permissions.py

from rest_framework import permissions

class IsOwnerOrReadOnly(permissions.BasePermission):
    """
    自定義permission 保證只有全部者可以編寫。
    """
    def has_object_permission(self,request,view,obj):
        #任何請求都擁有讀權限
        #因此咱們永遠容許GET,HEAD,OPTIONS requests
        if request.method in permissions.SAFE_METHODS:
            return True
        return obj.owner == request.user

如今,咱們將咱們自定義的permission添加到snippetDetailview class中:

permission_classes = (permissions.IsAuthenticateOrReadOnly,IsOwnerOrReadOnly)

API認證

由於咱們如今爲咱們的API設置了permission,若是咱們須要寫任何snippets,咱們須要認證咱們的請求。咱們尚未設置任何身份認證,因此默認的認證是sessionAuthenticationBasucAuthentication.當咱們經過瀏覽器與API交互時,咱們能夠登陸,而後瀏覽器的session將會爲咱們的請求提供身份認證。若是咱們以編程的方式於API進行交互,咱們須要在每一個請求上顯式地提供身份驗證憑證。
若是咱們嘗試建立一個snippet,就會獲得一個錯誤:

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"
}
相關文章
相關標籤/搜索