Django REST Framework Tutorial 4:認證與權限(中文版教程)by hillfree


摘要: 跟着Django的教程走例子,遇到了Django REST Framework的內容,可惜教程用的版本有點老,在Django1.5,Python3.3下例子都通不過。在http://django-rest-framework.org的官網上有教程,我看了看,似乎只有前兩部分的中文翻譯,就趁着自己看也翻一下后面的部分,自己學習,供大家參考:如有錯訛,請多指教。

原始來源:http://django-rest-framework.org/tutorial/4-authentication-and-permissions.html

 

教程4: 認證和權限 Authentication & Permissions

 

目前為止,我們的API對誰能編輯或刪除snippet(代碼片段)還沒有任何限制。我們將增加一些擴展功能來確保以下:

  • snippets總關聯一個創建者;
  • 只有認證后的用戶才能創建一個snippets.
  • 只有創建者才能更新或刪除snippet;
  • 非認證請求只擁有只讀權限。

為model增加信息

我們先需要對Snippet的model做些修改。首先,增加一些fields. 其中一個用來表示創建者,另一個用來存儲代碼中的HTML高亮。

在模型中增加這兩個字段。

1 owner = models.ForeignKey('auth.User', related_name='snippets')
2 highlighted = models.TextField()

我們還需要確保model存儲時,我們能生成高亮字段內容,這里使用 pygments 代碼高亮庫。

首先需要一些額外的imports:

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)

 

這些完成后還需要更新一下數據庫里面的表。通常我們要創建一個數據庫遷移(database migration)來完成,但教程中,我們就只是刪除數據庫然后重新創建:

rm tmp.db
python ./manage.py syncdb

 

你可能想創建一些其他用戶來測試這些API,最快捷的方式是利用 createsuperuser 命令。

python ./manage.py createsuperuser

 

為用戶模型增加endpoints

現在我們需要一些用戶,我們最好把用戶呈現也增加到API上,創建一個新的serializer很容易:

from django.contrib.auth.models import User

class UserSerializer(serializers.ModelSerializer):
    snippets = serializers.PrimaryKeyRelatedField(many=True)

    class Meta:
        model = User
        fields = ('id', 'username', 'snippets')

 

因為 'snippets' 和用戶model是反向關聯(reverse relationship,即多對一),所以在使用 ModelSerializer時並不會缺省加入,所以我們需要顯式的來實現。

我們還需要創建一些views,對用戶呈現而言,我們最好使用只讀的view,所以使用 ListAPIView 和 RetrieveAPIView 泛型類Views。

class UserList(generics.ListAPIView):
    model = User
    serializer_class = UserSerializer

class UserDetail(generics.RetrieveAPIView):
    model = User
    serializer_class = UserSerializer

 

最后,我們需要修改URL conf:

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

 

把Snippets與Users關聯

現在,如果我們創建一個code snippet,還沒有方法指定其創建者。User並沒有作為序列化內容的一部分發送,而是作為request的一個屬性。

這里的處理方法是重載snippet view中的 .pre_save() 方法,它可以讓我們處理request中隱式的信息。

在 SnippetList 和 SnippetDetail 的view類中,都需要添加如下的方法:

def pre_save(self, obj):
    obj.owner = self.request.user

 

更新 serializer

現在snippets已經和創建者關聯起來了,我們接下來還需要更新SnippetSerializer,在其定義中增加一個新的字段:

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

Note: 確定你在嵌入類Meta的字段列表中也加入了 'owner'。

這個字段所做的十分有趣。source 參數表明增加一個新字段,可以指定序列化實例任何屬性。它可以采用如上的點式表示(dotted notation),這時他可以直接遍歷到指定的屬性。在Django's template中使用時,也可以采用類似的方式。

我們增加字段是一個無類型 Field 類,而我們之前的字段都是有類型的,例如 CharFieldBooleanField etc... 無類型字段總是只讀的,它們只用在序列化表示中,而在反序列化時(修改model)不被使用。 

給view增加權限控制

現在代碼片段 snippets 已經關聯了用戶,我們需要確保只有認證用戶才能增、刪、改snippets.

REST framework 包括許多權限類可用於view的控制。這里我們使用 IsAuthenticatedOrReadOnly, 它可確保認證的request獲取read-write權限,而非認證的request只有read-only 權限.

現需要在views模塊中增加 import。

from rest_framework import permissions

然后需要在 SnippetList 和 SnippetDetail view類中都增加如下屬性:

permission_classes = (permissions.IsAuthenticatedOrReadOnly,)

 

為可瀏覽API(Browseable API)增加login

如果你打開瀏覽器,訪問可瀏覽API,你會發現只有登錄后才能創建新的snippet了。

我們可以編輯URLconf來增加一個登錄view。首先增加新的import:

from django.conf.urls import include

然后,在文件末尾增加一個pattern來為browsable API增加 login 和 logout views.

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

 

具體的, r'^api-auth/' 部分可以用任何你想用的URL來替代。這里唯一的限制就是 urls 必須使用'rest_framework' 命名空間。

現在如果你打開瀏覽器,刷新頁面會看到頁面右上方的 'Login' 鏈接。如果你用之前的用戶登錄后,你就又可以創建 snippets了。

一旦你創建了一些snippets,當導航至'/users/'時,你會看到在每個user的snippets字段都包含了一系列snippet的pk。

對象級別的權限

我們希望任何人都可以瀏覽snippets,但只有創建snippet的用戶才能編輯或刪除它。

為了實現這個需求,我們需要創建定制的權限(custom permission)。

在 snippets 應用中,創建一個新文件: 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 屬性:

permission_classes = (permissions.IsAuthenticatedOrReadOnly,
                      IsOwnerOrReadOnly,)

別忘了import 這個IsOwnerOrReadOnly 類。

from snippets.permissions importIsOwnerOrReadOnly

 

現在打開瀏覽器,你可以看見 'DELETE' 和 'PUT' 動作只會出現在那些你的登錄用戶創建的snippet頁面上了.

通過API認證

我們已經有了一系列的權限,如果我們需要編輯任何snippet,我們需要認證我們的request。因為我們還沒有建立任何 authentication classes, 所以目前是默認的SessionAuthentication 和 BasicAuthentication在起作用。

當我們通過Web瀏覽器與API互動時,我們登錄后、然后瀏覽器session可以為所有的request提供所需的驗證。

如果我們使用程序訪問這些API,我們則需要顯式的為每個request提供認證憑證(authentication credentials)。

如果我們試圖未認證就創建一個snippet,將得到錯誤如下:

curl -i -X POST http://127.0.0.1:8000/snippets/ -d "code=print 123"

{"detail": "Authentication credentials were not provided."}

 

如果我們帶着用戶名和密碼來請求時則可以成功創建:

curl -X POST http://127.0.0.1:8000/snippets/ -d "code=print 789" -u tom:password

{"id": 5, "owner": "tom", "title": "foo", "code": "print 789", "linenos": false, "language": "python", "style": "friendly"}

小結

我們已經為我們的Web API創建了相當細粒度的權限控制和相應的系統用戶。

在教程第5部分 part 5 ,我們將把所有的內容串聯起來,為我們的高亮代碼片段創建HTML節點,並利用系統內的超鏈接關聯來提升API的一致性表現。

【translate by hillfree @ 2013年3月26日10:14:55】


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM