歡迎訪問個人我的網站:www.comingnext.cnhtml
按照前面幾篇文章裏那樣作,使用Django編寫RESTful API的基本功能已經像模像樣了。咱們能夠經過不一樣的URL訪問到不一樣的資源,經過不一樣的HTTP請求來實現對資源的不一樣操做。python
可是如今咱們的API還有一個很明顯的缺陷,那就是沒有認證和權限功能,任何資源都會任何用戶被隨意更改,因此咱們要改進程序,實現如下功能:sql
首先,咱們想讓snippets都和它們的建立用戶關聯起來,因此咱們天然的要在Snippet模型添加一個owner字段來表示。另外,咱們還添加一個highlighted字段用來實現代碼高亮,修改snippets/models.py的Snippet:shell
owner = models.ForeignKey('auth.User', related_name='snippets', on_delete=models.CASCADE) highlighted = models.TextField()
想要實現代碼高亮,固然不是上面一行代碼就搞定了,它如今還只是一個普通的字段而已。咱們要作的是在保存的時候,也就是當執行save()時, 咱們使用pygments生成高亮後的HTML,仍是在model.py,首先導入相關的庫:數據庫
from pygments.lexers import get_lexer_by_name from pygments.formatters.html import HtmlFormatter from pygments import highlight
而後在Snippet類中添加save()方法:django
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)
在保存數據的時候就會執行上面這個方法,整個方法的功能如註釋所示,在這一篇文章中還不會具體的展現這個功能,在接下來的文章中會展現。api
修改了模型固然須要同步一下數據庫了,在這裏咱們和官方文檔同樣把數據庫刪了在從新生成,首先把工程目錄下的db.sqlite3以及snippets下的migrations文件夾刪除,而後再執行遷移步驟:瀏覽器
python manage.py makemigrations snippets
python manage.py migrate
同時,因爲咱們想要實現的是訪問各個snippet時顯示相應的建立者,因此這裏須要建立幾個不一樣的帳戶稍後才能夠顯示。安全
python manage.py createsuperuser
原理和以前的SnippetSerializer基本同樣,在snippets/serializers.py中添加一個User序列化器:ide
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 = serializers.PrimaryKeyRelatedField(many=True, queryset=Snippet.objects.all())
由於snippets在User模型中是一個反向關係,在使用ModelSerializer類時默認狀況是不會包括這個關係,就是說經過Snippet的owner能查詢到User,而User這邊查詢不到一個用戶建立的snippet,因此咱們須要手動爲用戶序列添加這個字段。
弄好了User的序列化器,接着就要讓其可以顯示出來,因此要添加相關的視圖類,編輯view.py:
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.objects.all() serializer_class = UserSerializer
寫好了視圖函數,想要經過URL訪問到它們,確定是配置一下路由分發啦,編輯snippets/urls.py,添加下面的匹配模式:
url(r'^users/$', views.UserList.as_view()), url(r'^users/(?P<pk>[0-9]+)/$', views.UserDetail.as_view()),
到了這裏,若是像以前那樣建立代碼段的話,咱們還不能把Snippets和Users關聯起來。由於在使用的時候User的數據是經過request傳入的,而不是以序列化的數據傳遞過來。
而咱們剛纔添加了一個owner做爲外鍵,這個時候就要看到它的用處了,編輯view.py,爲SnippetList視圖類添加一個方法:
class SnippetList(generics.ListCreateAPIView): queryset = Snippet.objects.all() serializer_class = SnippetSerializer permission_classes = (permissions.IsAuthenticatedOrReadOnly,) def perform_create(self, serializer): serializer.save(owner=self.request.user)
這個perform_create() 可讓用戶在經過POST請求建立一個新的Snippet時,在保存新的Snippet數據的時候會把request中的user賦值給Snippet的owner。等下具體使用的時候就能夠輕鬆的理解了。
上一步已經把二者關聯起來了,owner會在建立新的Snippet的時候擁有User的各個屬性,那麼在API中要讓owner顯示id仍是用戶名,爲了提升可讀性,答案固然是顯示用戶名了,因此咱們在SnippetSerializer 下面增長一個字段:
owner = serializers.ReadOnlyField(source='owner.username')
這裏的source參數就指定了哪一個屬性用於填充字段,爲了在使用的時候顯示owner,可是還要把它添加進Meta類裏面,因此整個SnippetSerializer以下:
class SnippetSerializer(serializers.ModelSerializer): # 這裏可使用也 CharField(read_only=True) 來替換 owner = serializers.ReadOnlyField(source='owner.username') class Meta: model = Snippet fields = ('id', 'title', 'code', 'linenos', 'language', 'style','owner')
如今Snippet和User已經關聯起來而且是可瀏覽的。接下來咱們要實現的及時權限的問題了。也就是咱們一開始說的幾點中的:
只有通過身份驗證(登陸)的用戶才能夠建立snippet
只有建立該snippet的用戶才能夠對其進行更改或者刪除
未經驗證的用戶只具備訪問(只讀)的功能
首先在views.py導入一個庫:
from rest_framework import permissions
接着爲SnippetList 和 SnippetDetail添加權限判斷,在這兩個視圖類中都加入:
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
這裏要特別注意,有一個坑,就是那個逗號必定要加上去,否則就會報錯。
這行代碼的做用就是判斷當前用戶是否爲該Snippet的建立者,而其餘用戶只有只讀屬性,就是隻能查看。
剛纔添加了權限判斷,若是沒有登陸用戶,那就至關於遊客啦,什麼功能都沒有隻能看,因此在瀏覽器瀏覽API的時候就須要登陸 功能。在這裏,強大的django-rest-framework又爲咱們作了不少事情,想要在添加登陸按鈕和頁面,只須要修改一個rest_tutorial/urls.py,添加一個URL匹配:
urlpatterns += [ url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')), ]
這裏的r'^api-auth/'你能夠設置成任意你喜歡的,可是命名空間必定要相同,就是namespace='rest_framework'。
好了,如今打開瀏覽器,就能夠看到在咱們的API頁面的右上角有一個登陸的按鈕,點擊以後就可使用以前建立的用戶登陸了。
這個時候訪問單個用戶的詳情,就能夠看到該用戶建立的全部Snippet的id值(須要先建立好幾個Snippet,能夠按照本系列第一篇文章中在shell模式中的方法來建立)。好比訪問:
http://127.0.0.1:8000/users/2/
能夠看到:
接着咱們要實現的是讓全部的Snippet能夠被全部人訪問到,可是每一個Snippet只有其建立者才能夠對其進行更改、刪除等操做。
所以,咱們須要設置一下自定義權限,使每一個Snippet只容許其建立者編輯它。在snippets目錄下新建一個permissions.py:
from rest_framework import permissions class IsOwnerOrReadOnly(permissions.BasePermission): """ 使每一個Snippet只容許其建立者編輯它 """ def has_object_permission(self, request, view, obj): # 任何用戶或者遊客均可以訪問任何Snippet,因此當請求動做在安全範圍內, # 也就是GET,HEAD,OPTIONS請求時,都會被容許 if request.method in permissions.SAFE_METHODS: return True # 而當請求不是上面的安全模式的話,那就須要判斷一下當前的用戶 # 若是Snippet全部者和當前的用戶一致,那就容許,不然返回錯誤信息 return obj.owner == request.user
代碼的邏輯已在註釋中,簡單說就是提供判斷功能,而後咱們要把它運用起來,在view.py中的SnippetDetail 修改一下:
class SnippetDetail(generics.RetrieveUpdateDestroyAPIView): queryset = Snippet.objects.all() serializer_class = SnippetSerializer permission_classes = (permissions.IsAuthenticatedOrReadOnly, IsOwnerOrReadOnly,)
注意要導入IsOwnerOrReadOnly類:
from snippets.permissions import IsOwnerOrReadOnly
如今用瀏覽器打開單個Snippet詳情頁,若是你當前登陸的用戶是這個Snippet的建立者,那你會發現多了DELETE和PUT兩個操做,好比訪問http://127.0.0.1:8000/snippets/2/,效果以下:
因爲如今咱們還沒使用authentication 類,因此項目目前仍是使用默認的SessionAuthentication 和 BasicAuthentication.
在使用瀏覽器訪問API的時候,瀏覽器會幫咱們保存會話信息,因此當權限知足時就能夠對一個Snippet進行刪除或者更改,或者是建立一個新的Snippet。
當若是是經過命令行來操做API,咱們就必須在每次發送請求的時候添加受權信息,也就是用戶名和密碼,沒有的話就會報錯,好比:
http POST http://127.0.0.1:8000/snippets/ code="print 123" { "detail": "Authentication credentials were not provided." }
正確的操做以下:
http -a username1:password POST http://127.0.0.1:8000/snippets/ code="print 789" { "id": 1, "owner": "username1", "title": "", "code": "print 789", "linenos": false, "language": "python", "style": "friendly" }
咱們能夠看出owner就是提交過來的用戶名,這就是上面代碼的功能體現:
def perform_create(self, serializer): serializer.save(owner=self.request.user)
經過實際使用更能理解程序,owner會在一個用戶建立Snippet時獲得該用戶的信息就是這麼來的。
本文地址:http://www.cnblogs.com/zivwong/p/7456591.html
做者博客:ziv歡迎轉載,請在明顯位置給出出處及連接