在這以前我寫過一篇關於Django與Drf快速開發實踐的博客,Django快速開發實踐:Drf框架和xadmin配置指北,粗略說了一下Drf配置和基本使用,不過裏面只是涉及到最基本的CRUD,在正常的後端開發中涉及的諸如認證和權限、消息隊列、緩存之類的操做,上一篇博客並無涉及,此次開發我仔細了看了官方文檔的這幾個部分,把這部分的功能完善了起來。html
Drf的設計頗有Django的味道,(畢竟就是伴生框架嘛)封裝了不少功能,稍微配置一下就能夠用,這點在快速開發方面真的好評。java
開始進入正題。python
任何系統都離不開認證和受權,Drf內置一套強大的鑑權系統,框架提供了幾種基本的認證和權限控制方式,小型系統基本夠用,還能夠自定義權限中間件,很方便就能實現節流這樣的功能。linux
Drf內置的四種API認證方式,基本信息我作了一個表格:android
認證方式 | 說明 |
---|---|
BasicAuthentication | 每次提交請求的時候附加用戶名和密碼來進行認證 |
TokenAuthentication | 每次提交請求的時候在HTTP headers裏附加Token進行認證 |
SessionAuthentication | 用戶登陸以後系統在cookies存入sessionid進行認證 |
RemoteUserAuthentication | 經過web服務器認證(apache/nginx這些) |
我選擇的是基於Token的認證,客戶端登陸以後維護一個token,每次請求附加到HTTP headers,還算是方便。nginx
Drf還能夠自定義認證方式,只要繼承authentication.BaseAuthentication
這個類而後重寫authenticate
方法就行了。這裏只簡單說下步驟,具體的參考官方文檔。web
BaseAuthentication
、重寫authenticate
方法authenticate()
返回值
None
:當前認證無論,等下一個認證來執行raise exceptions.AuthenticationFailed('用戶認證失敗')
request.user
、元素2複製給request.auth
在settings.py
中能夠配置默認的認證方式,這裏我添加了三個:apache
REST_FRAMEWORK = { # 身份驗證 'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework.authentication.BasicAuthentication', 'rest_framework.authentication.TokenAuthentication', 'rest_framework.authentication.SessionAuthentication', ) }
固然也支持各類第三方的認證框架,好比下面這些:django
使用這些認證方式,認證經過後,在views裏面request.user
就是一個Django用戶對象,若是未認證,就是一個AnonymousUser
對象。json
接下來講說權限
Drf的接口權限有如下幾種:
通常來講小網站用到DjangoModelPermissions
就是夠用的,或者乾脆簡單一點,用IsAuthenticated
和queryset
限定請求的數據便可。
介紹完了基本概念,來看看代碼中是如何操做的。
對於操做用戶信息的viewset,我只用了permissions.IsAuthenticated
這個權限,而後覆蓋了ReadOnlyModelViewSet
的get_queryset
方法,把queryset變成只包括當前用戶,這樣就保證了一個用戶只能操做本身的信息。
from rest_framework import authentication, permissions, viewsets class UserViewSet(viewsets.ReadOnlyModelViewSet): permission_classes = [permissions.IsAuthenticated] serializer_class = UserSerializer def get_queryset(self): return User.objects.filter(pk=self.request.user.pk)
viewset的action一樣可使用權限,加在裝飾器的參數上便可:
@action(detail=True, methods=['GET'], permission_classes=[permissions.IsAuthenticated]) def some_actions(self, request, pk=None): dosomething return Response(SomeSerializer(some_data, many=True).data)
這裏提一下裝飾器的detail參數,這個表明了是對列表操做仍是對單個對象操做,True就是對單個對象。
從請求的URL上應該能夠很好理解。
http://hostname/viewset/some_action/
http://hostname/viewset/1/some_action/
這部分的參考資料(優先閱讀官方文檔):
Drf的ApiView至關於Django的View,每一個ViewSet是一組Restful的接口,看看viewset的代碼,其中定義了6個action,對應get、post、put、delete這些http方法。
class UserViewSet(viewsets.ViewSet): def list(self, request): pass def create(self, request): pass def retrieve(self, request, pk=None): pass def update(self, request, pk=None): pass def partial_update(self, request, pk=None): pass def destroy(self, request, pk=None): pass
除了自帶的這些,還能夠定義額外的action,這個在前文已經提過了。
ApiView的話,使用起來和Django的差很少,一個apiview只能對應一個url,經過重寫get
、post
這些方法能夠實現不一樣的http方法響應。
ApiView和ViewSet一樣經過在類字段中加入authentication_classes
和permission_classes
實現認證和受權。
Drf和Django同樣自帶分頁功能,很好用(固然也支持使用第三方的分頁功能)。
首先進行配置(不配置的話使用默認配置),這裏我設置每頁顯示十條記錄:
REST_FRAMEWORK = { # 設置分頁 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', 'PAGE_SIZE': 10, }
使用得最多的ModelViewSet
已經自帶分頁了,這個咱們不用操心,不過若是本身定義了action來返回列表數據的話,就沒有分頁,這時候要用paginate_queryset
方法來處理。
代碼以下:
@action(detail=False) def tag(self, request): queryset = SomeModel.objects.all().order_by('-add_time') page = self.paginate_queryset(queryset) if page is not None: return self.get_paginated_response(self.get_serializer(page, many=True).data) return Response(self.get_serializer(queryset, many=True).data)
能夠看出Drf自動處理了不一樣頁面的請求,不用像Django同樣本身從GET或者POST數據裏讀取page,分頁相關的方法直接在viewset對象裏面,很是方便。
Drf支持不少文檔插件,這裏我用一下比較傳統的coreapi文檔,首先配置:
REST_FRAMEWORK = { # 文檔 'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema', }
在使用以前還須要使用pip安裝coreapi:
pip install coreapi
全部的ViewSet只要用了Router註冊的話都默認添加到文檔裏,不過ApiView的話是要本身添加的。
from rest_framework.schemas.coreapi import AutoSchema class SomeView(APIView): schema = AutoSchema() def post(self, request): data = dosomething() return Response(data)
這樣就能夠了,別忘了註冊路由,路由我在下文統一介紹。
配置完成以後文檔的效果應該是下圖這樣,這個頁面能夠測試請求,還能夠以各類方式登陸認證,比Drf默認的界面豐富一些~
使用Drf框架能夠有兩種路由方式,一種是Drf的路由,一直是Django的路由,兩種搭配使用。
from rest_framework import routers router = routers.DefaultRouter() router.register('users', UserViewSet, basename='api-users') router.register('user-profiles', UserProfileViewSet, basename='api-user-profiles') router.register('tags', TagViewSet, basename='api-tags') router.register('categories', CategoryViewSet, basename='api-categories') router.register('articles', ArticleViewSet, basename='api-articles')
定於完成以後要添加到Django的urlpatterns
裏面。
urlpatterns = [ path('api/', include(router.urls)), ]
對於ApiView,路由和Django的ClassBaseView差很少:
urlpatterns = [ path('login/', views.LoginView.as_view()), path('signup/', views.SignUpView.as_view()), ]
from rest_framework.authtoken import views as authtoken_view from rest_framework.documentation import include_docs_urls urlpatterns = [ path('api-auth/', include('rest_framework.urls', namespace='rest_framework')), path('api-token-auth/', authtoken_view.obtain_auth_token), path('api-docs/', include_docs_urls(title='One Cat Docs')), ]
序列化器也是Drf的一個重要概念,和Django的Form很像,做用是將Model對象的數據轉換爲json、xml之類的結構化數據。
使用起來很簡單,我通常都是這麼定義
class CategorySerializer(serializers.ModelSerializer): class Meta: model = Category fields = '__all__'
使用__all__
直接把全部模型字段都包括進去,針對外鍵等關係字段,咱們須要作一些其餘處理。
這裏簡單介紹一下我經常使用的幾種關係字段處理方式
StringRelatedField
:將關係字段顯示爲一個字符串,這個字符串取決於該模型定義的__str__
方法PrimaryKeyRelatedField
:顯示成該關係字段的主鍵,這個也是默認的具體的使用方式能夠在官網找到~
下面再介紹幾個經常使用的參數:
read_only
:通常針對不想被修改的字段可使用,好比說用戶idmany
:用於多對多關係或者一對多,會將全部引用序列化爲一個列表其實就是一個自定義的認證過程。
Drf內置有BaseThrottle
、 SimpleRateThrottle
,後者是前者的之類。
BaseThrottle
須要本身寫allow_request
和wait
方法,控制粒度更細 SimpleRateThrottle
重寫get_cache_key
和設置scope
名稱就能夠,更簡單 SimpleRateThrottle
代碼以下:
from rest_framework.throttling import SimpleRateThrottle class VisitThrottle(SimpleRateThrottle): '''匿名用戶60s只能訪問三次(根據ip)''' scope = 'throttle' #這裏面的值,本身隨便定義,settings裏面根據這個值配置throttle def get_cache_key(self, request, view): #經過ip限制節流 return self.get_ident(request) class UserThrottle(SimpleRateThrottle): '''登陸用戶60s能夠訪問10次''' scope = 'userThrottle' #這裏面的值,本身隨便定義,settings裏面根據這個值配置userThrottle def get_cache_key(self, request, view): return request.user.user_id
BaseThrottle
代碼以下:
from rest_framework.throttling import BaseThrottle import time VISIT_RECORD = {} #保存訪問記錄 class VisitThrottle(BaseThrottle): '''60s內只能訪問3次''' def __init__(self): self.history = None #初始化訪問記錄 def allow_request(self,request,view): #獲取用戶ip (get_ident) remote_addr = self.get_ident(request) ctime = time.time() #若是當前IP不在訪問記錄裏面,就添加到記錄 if remote_addr not in VISIT_RECORD: VISIT_RECORD[remote_addr] = [ctime,] #鍵值對的形式保存 return True #True表示能夠訪問 #獲取當前ip的歷史訪問記錄 history = VISIT_RECORD.get(remote_addr) #初始化訪問記錄 self.history = history #若是有歷史訪問記錄,而且最先一次的訪問記錄離當前時間超過60s,就刪除最先的那個訪問記錄, #只要爲True,就一直循環刪除最先的一次訪問記錄 while history and history[-1] < ctime - 60: history.pop() #若是訪問記錄不超過三次,就把當前的訪問記錄插到第一個位置(pop刪除最後一個) if len(history) < 3: history.insert(0,ctime) return True def wait(self): '''還須要等多久才能訪問''' ctime = time.time() return 60 - (ctime - self.history[-1])
#全局 REST_FRAMEWORK = { # 設置全局節流 "DEFAULT_THROTTLE_CLASSES":['api.utils.throttle.UserThrottle'], #全局配置,登陸用戶節流限制(10/m) # 設置訪問頻率 "DEFAULT_THROTTLE_RATES":{ 'throttle':'3/m', #沒登陸用戶3/m,throttle就是scope定義的值,經過IP地址 'userThrottle':'10/m', #登陸用戶10/m,userThrottle就是scope定義的值, 經過user_id } } # 局部:在類視圖中添加 throttle_classes = [VisitThrottle,]
大概就這些吧,官方文檔真的很詳細,看官方文檔能夠解決95%的疑問,加上Django和python自帶的快速開發生產力,寫起來太爽啦~
我整理了一系列的技術文章和資料,在公衆號「程序設計實驗室」後臺回覆 linux、flutter、c#、netcore、android、java、python 等可獲取相關技術文章和資料