Django restfulframework 開發相關知識 整理

 

目錄

前言

一句話歸納RESTful
一種設計風格,經過URL定位資源,HTTP動詞描述操做。旨在讓咱們更好更快地開發出web應用javascript

先後端分離

先後端分離帶來的優勢

  • 能夠爲多個不一樣客戶端提供同一個數據源
    不一樣類型的客戶端(pc端,ios端,安卓端)。保證之後開發時,後端代碼只要一套便可
  • ·可使得開發url變得更加簡潔
    由以前的get_xx,delete_xx,change_xx,add_xx 之間由一個新的xxs配合請求方法代替
  • 能夠實現先後端分離,並行開發
    傳統的網絡應用程序都是'重服務端',客戶端只是單純的展現數據。即便如今都仍是主流;
    可是因爲如今的客戶端發展很快,因此如今出現了漸漸着重客戶端,旨在減小服務端的職責

實現先後端分離的方法

  •  模式1:後臺開發先搞好API文檔給前端開發。
    前端開發安裝API文檔先行寫好業務邏輯,可是缺點是前端拿不到測試數據,意味着不能測試邏輯直到真正的API完成後才行。這種模式效率比較低下。
  •  模式2:先後端開發作好返回數據類型的約定,前端用假數據模擬這一個後端接口(mock)。
    這種模式先後端能夠並行開發,效率高。可是缺點是要本身編寫僞造文件(xx.json),還要啓動一個簡易服務器模擬(防止跨域),
    而且完成以後還要刪文件,更改請求路徑,麻煩。最重要的是隻能get不能post
  • 模式3:使用第三方工具造假例如mock.js
     其原理是攔截掉請求,返回一個造假數據。(也就是說ajax的請求路徑能夠一開始就寫上線版本的)。缺點是沒法模擬服務器端記錄數據,例如:用戶發表一篇博文,而後再發表第二篇 
  • 模式4:json-server,應該是最好的一種方式
    只要保證json-server裏面的資源和服務器端的資源是同樣的,另外都是RESTful風格,就能夠無縫切換

RESTful十大規範

協議規範

  HTTPS前端

域名規範

目的是讓人一看url就知道這是xx網站請求數據的接口vue

  • 子域名方式

   例如:https://www.love.com # 用於給用戶訪問java

      https:// api.love.com # 用於請求json數據
   注意:該方法須要解決跨域問題,通常用cors解決,表現爲一個請求發兩次python

  • url方式

   例如:https:/ /www.love.comios

   https:/ /www .love.com/api/web

版本表示規範

  經常使用於版本過渡使用
  https:// api.example.com/v1/ajax

url使用名詞

  經過url定位資源數據庫

  即面向資源編程,面向資源編程便是面向一類數據去編程。django

  現實場景,有不少類數據,例如,產品類、訂單類、用戶類。每一個資源實際上對應一種資源。例如:面向產品類數據編程實際便是面向產品表所記錄的數據去編程。面向資源編程最重要是怎樣表示資源
  即地址即資源(用地址的方式去描述一個資源),看到這個地址,就感受看到這個地址對應的資源是什麼

 

http請求動詞

  對於資源的操做只有增刪改查。在HTTP協議中對每個請求url都會有不一樣的謂詞GET/POST/PUT/PATCH/DELETE,對於每個表明資源的url配對不一樣的謂詞時會產生不一樣意義
  例如:對 http://api.demo.com/users 用GET方式去請求,則是請求全部用戶數據,換作POST方式,則是要新增一個用戶

  • GET(SELECT):從服務器取出資源(一項或多項)。
  • POST(CREATE):在服務器新建一個資源。
  • PUT(UPDATE):在服務器更新資源(客戶端提供改變後的完整資源)。
  • PATCH(UPDATE):在服務器更新資源(客戶端提供改變的屬性)。
  • DELETE(DELETE):從服務器刪除資源。

過濾  

  若是記錄數量不少,服務器不可能都將它們返回給用戶。API應該提供參數,過濾返回結果。經過在url上GET傳參的形式傳遞搜索條件

https://api.example.com/v1/zoos?limit=10:指定返回記錄的數量

https://api.example.com/v1/zoos?offset=10:指定返回記錄的開始位置

https://api.example.com/v1/zoos?page=2&per_page=100:指定第幾頁,以及每頁的記錄數

https://api.example.com/v1/zoos?sortby=name&order=asc:指定返回結果按照哪一個屬性排序,以及排序順序

https://api.example.com/v1/zoos?animal_type_id=1:指定篩選條件

 

狀態碼

  • 成功,200系列

  200 OK - [GET]:服務器成功返回用戶請求的數據,該操做是冪等的(Idempotent)。

  201 CREATED - [POST/PUT/PATCH]:用戶新建或修改數據成功。

  204 NO CONTENT - [DELETE]:用戶刪除數據成功。

  • 重定向,300系列

  301:臨時重定向

  302:永久重定向

  • 客戶端錯誤,400系列

  400 :用戶發出的請求有錯誤,問題在用戶的請求,該操做是冪等的。

  401 :沒有權限(令牌、用戶名、密碼錯誤)。

  403:權限不夠

  404:資源不存在,該操做是冪等的。

  • 服務端錯誤,500系列

  500 :服務器發生錯誤

 注:實際生產中,狀態碼與code同時使用,通常和前端討論時要問前端對狀態碼有沒有要求
  

錯誤信息

  若是狀態碼是4xx,就應該向用戶返回出錯信息。通常來講,返回的信息中將error做爲鍵名,出錯信息做爲鍵值便可。

{
    error: "Invalid API key"
}

請求方法返回

GET /collection:返回資源對象的列表(數組)

GET /collection/resource:返回單個資源對象

POST /collection:返回新生成的資源對象

PUT /collection/resource:返回完整的資源對象

PATCH /collection/resource:返回完整的資源對象

DELETE /collection/resource:返回一個空文檔

Hypermedia API

最好作到Hypermedia,即返回結果中提供連接,連向其餘API方法,使得用戶不查文檔,也知道下一步應該作什麼。代碼上不須要拼接新的連接
例如:訪問orders,返回包含所有訂單的列表,裏面的每個元素都提供一個得到其詳細信息的url

跨域

cors

瀏覽器支持的機制,當服務器端給瀏覽器端返回響應的響應頭的時候,瀏覽器就會讓其經過驗證

判斷簡單請求和複雜請求

            1、請求方式:HEAD、GET、POST
            2、請求頭信息:
                Accept
                Accept-Language
                Content-Language
                Last-Event-ID
                Content-Type 對應的值是如下三個中的任意一個
                                        application/x-www-form-urlencoded
                                        multipart/form-data
                                        text/plain
         
        注意:同時知足以上兩個條件時,則是簡單請求,不然爲複雜請求

預檢

對待複雜請求時使用,瀏覽器會先發一次option請求預檢,預檢成功以後才發真正要發的請求
由於即便容許指定域名跨域,也會限制不在指定範圍內的請求頭或請求方法。因此先發一次預檢過來驗證一下你這個請求頭或請求方法能不能被我所接受。

from django.utils.deprecation import MiddlewareMixin


class CORSMiddleware(MiddlewareMixin):
    def process_response(self, request, response):
        # 添加響應頭

        # 容許你的域名來獲取個人數據
        response['Access-Control-Allow-Origin'] = 'http://localhost:8080'  # 如果設置cookie跨域,則這裏不能爲'*'

        # # 容許你攜帶Content-Type請求頭
        response['Access-Control-Allow-Headers'] = """Content-Type,Accept-Encoding,Access-Control-Request-Headers,Access-Control-Request-Method,Connection,Host,Origin,User-Agent,Content-Disposition"""
        # # 容許你發送DELETE,PUT
        response['Access-Control-Allow-Methods'] = "GET,POST,DELETE,PUT"
" # 容許使用cookie,使用後,就跟日常沒有跨域同樣使用。而vue-cookie只是應用在須要咱們在在客戶端手動操縱cookie的時候 response['Access-Control-Allow-Credentials'] = 'true' return response

 

跨域cookie

在跨域過程當中,Cookie是默認不發送的。就算後端返回Cookie字段,瀏覽器也不會保存Cookie,更不會在下一次訪問的時候發送到後端了。

跨域cookie的設置
第一步,在ajax中設置,withCredentials: true。
第二步,服務端的Access-Control-Allow-Origin 不能夠設置爲"*",必須指定明確的、與請求網頁一致的域名
第三步,服務端的 Access-Control-Allow-Credentials: true,表明服務器接受Cookie和HTTP認證信息。由於在CORS下,是默認不接受的。

  • 對於XMLHttpRequest的Ajax請求
var xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.withCredentials = true; // 攜帶跨域cookie
xhr.send();
  • 對於JQuery的Ajax請求
$.ajax({
    type: "GET",
    url: url,
    xhrFields: {
        withCredentials: true // 攜帶跨域cookie
    },
    processData: false,
    success: function(data) {
        console.log(data);  
    }
});
  • 對於axios的Ajax請求
axios.defaults.withCredentials=true; // 讓ajax攜帶cookie

代理服務器

有兩種方法
正向代理(部署一個和瀏覽器同源的代理服務器進行轉發,例如python中使用request進行轉發,),因爲服務器端沒有同源策略限制,不管怎樣的數據都會收到
反向代理(讓服務器得到指定的同源域名),經過修改Nginx的配置文件,將指定的不一樣源域名代理到當前服務器

通常的代理服務器指的是正向代理,vue的配置以下

vueconfig/index.js

 

代理服務器的缺點
使用代理服務器的缺點是對不一樣源資源的轉發請求,若是同時多個用戶進行跨域請求,由於服務器內部須要進行額外的HTTP請求,那麼服務器端的處理壓力降會變大,從而致使阻塞等一系列性能問題,如需更好的方案,仍是得使用Nginx等反向代理服務器進行端口代理處理。


restfulframework 

restfulframework是基於Django的一個組件,目的是幫助開發高效開發出RESTful風格的web後端

因爲RESTful須要多種不一樣的請求方法,所以視圖模式最好使用CBV實現,由於CBV是基於反射實現的,原理是根據請求方式不一樣,執行不一樣的方法。
CBV的流程是:url路由->as_view方法->dispatch(反射,執行其成員函數)

restfulframework的APIView是這個組件的核心,繼承django基礎的View,在這基礎上封裝了不少諸如認證,權限等小組件。
restfulframework流程以下:

請求進來->走dispatch->版本->認證->權限->節流
->藉由反射執行視圖->藉由解析器拿請求數據->藉由序列化器進行數據校驗
->根據業務邏輯拿數據庫數據->藉由序列化器格式化數據庫數據->若是數據量大,藉由分頁器分頁->藉由渲染器在瀏覽器渲染不一樣的顯示
除了這些,restframework還有路由組件,視圖組件


APiView認證組件的源碼剖析

  第一步:進來就走dispatch
  第二步:dispatch裏面對原生的request進行進一步封裝
    第三步:而後執行self.initial(request)
  第四步:self.initial裏面執行認證self.perform_authticate
  第五步:self.perform_authticate執行request.user
  第六步:request.user內部將全部的認證類(全局/局部),並經過列表生成式建立認證對象,
  第七步:最後遍歷取出認證對象並調用其authenticate方法一一進行驗證

  附:使用django-restfulframework注意點

  • APIView的處理視圖的第一個參數request是已經被rest framework將各類參數加工封裝後的request,真正的request這樣拿request._request。
    而且其裏面有這樣一個機制,當調用request.xx時,xx在request時就直接返回,若沒有,則去看看request._request.xx有沒有
  • APIView的as_view函數返回一個csrf_exempt (view),換句話說已經取消django默認的csrf驗證方式。
  • postman的一個標籤至關於一個瀏覽器

 

認證組件

首先要了解,在分佈式系統中,必需要從新考慮「登陸憑證」的問題。由於session共享的處理十分麻煩。
因此大多數都採用jwt的認證方式,即登陸成功後返回一個token做爲用戶的認證票據,能夠放在請求頭或者url GET參數中帶過來進行驗證
token的常見處理是,在數據庫中定義一個新的表來存儲。以用戶OnetoOne關聯

 

好,進入正題,restfulframework提供認證組件,這讓開發者省去寫裝飾器或者中間件去驗證用戶認證票據的流程。
開發者只須要定義符合指定接口的認證類,配置上便可使用。

  • 默認狀況下

   若是不指定任何的認證類,默認是

'DEFAULT_AUTHENTICATION_CLASSES'= (
   'rest_framework.authentication.SessionAuthentication',
   'rest_framework.authentication.BasicAuthentication'
     ),
# SessionAuthentication對經過login(request,user)登陸的用戶有CSRF檢測。
# BasicAuthentication不少老牌路由器登陸的方法。方式是使用瀏覽器自帶的"對話框」方式將用戶名密碼通過base64加密後放在請求頭裏面發過去
 如今基本被淘汰,直接寫在配置上沒有任何反應。
  • 認證組件大致使用流程
  1. 定義一個Authtication類繼承 BaseAuthentication
  2. 其內部覆寫一個authenticate方法,在裏面進行驗證流程
  3. 在APIView子類裏聲明authentication_classes =[Authtication,] 便可使得當前的APIView生效
  4. 或者也能夠在settings.py配置全局生效
  REST_FRAMEWORK = {
    # 全局使用的認證類
    "DEFAULT_AUTHENTICATION_CLASSES":['api.utils.auth.FirstAuthtication', ], #覆蓋框架定義全局認證類
    # "UNAUTHENTICATED_USER":lambda :"匿名用戶"
    "UNAUTHENTICATED_USER":None, # 匿名,request.user = None
    "UNAUTHENTICATED_TOKEN":None,# 匿名,request.auth = None
}
  • 認證類編寫細節
from rest_framework.authentication import BaseAuthentication
from rest_framework import exceptions    

class UserAuth(BaseAuthentication):
 
    def authenticate(self, request):
        token = request.query_params.get("token")
        usertoken= UserToken.objects.filter(token =token).first()
 
        if usertoken:
        # 只能返回None或指定元組,一旦其中一個認證對象返回了元組,則整個認證流程結束 
            return  usertoken.user, usertoken.token
        else:
           raise AuthenticationFailed({"code":1001,"error":"用戶認證失敗"}) 
            # raise  AuthenticationFailed("認證失敗!") # restframework內部會有專門的捕捉,並返回

    def authenticate_header(self,request):
    # 編寫當認證失敗時給瀏覽器返回的響應頭
      pass

 

 注意:認證類的authenticate返回

  •  若驗證成功,返回一個‘規定’的元組。元組的第一個元素給APIView裏面的request.user;第二個元素給APIView裏面request.auth,通常爲token
  •  若返回None,表示讓下一個驗證繼續處理
  •  注意若所有認證類都返回None,則框架會默認給request.user賦予AnonymousUser,request.auth 賦予None
  • 能夠在setting.py文件裏配置,當所有認證類都返回None時,默認最終返回什麼

權限組件

  • 權限組件也是跟認證組件一個機制
  • 局部配置:permission_classes = [MyPermission1, ]
  • 全局適用:
    REST_FRAMEWORK = {
    "DEFAULT_PERMISSION_CLASSES":['api.utils.permission.SVIPPermission']
    }
  • 編寫細節
    from rest_framework.permissions import BasePermission
            class SVIPPermission(BasePermission):   #按規範 須要繼承BasePermission
                message = "必須是SVIP才能訪問"
                def has_permission(self, request, view):
                    if request.user.user_type != 3:
                        return False
                    return True

    注意權限類的兩個方法:
      1.has_permission
       has_permission只能返回True和False,True表示輪到下一個權限對象檢查,False表示這個權限檢查不經過,終止檢查流程返回當前的Message信息。    
       自定權限類的has_permission若返回False會觸發異常,框架內部會捕捉異常並返回一個message信息。 

     2.針對GenericViewSet的 has_object_permission
      權限檢查有一個def has_object_permission(self, request, view, obj): 這個是用戶檢查用戶是否有操做這個對象obj的權限(粒度更小)
      這個調用時機是在,GenericViewSet(實際上是GenericView)的get_object裏面有一個check_object_permissions,他循環調用了權限檢查對象的has_object_permission檢查是否對這個對象有操做權限。
      換句話說,也就是在使用GenericViewSet,纔會使得has_object_permission起做用

 

頻率訪問限制組件

  • 局部:throttle_classes=[]
  • 全局:"DEFAULT_THROTTLE_CLASSES":["api.utils.throttle.UserThrottle"] ,注意:這裏的是全局的api函數共享一個頻率
  • 編寫細節:實現1分鐘只能訪問3次
    #VISTIT_RECORD字典能夠用django緩存替換
        import time
        VISIT_RECORD = {}
        class VisitThrottle(BaseThrottle):
        
            def __init__(self):
                self.history = None
        
            def allow_request(self,request,view):
                # 1. 獲取用戶IP
                remote_addr = self.get_ident(request)
        
                ctime = time.time()
                if remote_addr not in VISIT_RECORD:
                    VISIT_RECORD[remote_addr] = [ctime,]
                    return True
                history = VISIT_RECORD.get(remote_addr)
                self.history = history
        
                while history and history[-1] < ctime - 60:
                    history.pop()
        
                if len(history) < 3:
                    history.insert(0,ctime)
                    return True
        
                # return True    # 表示能夠繼續訪問
                # return False # 表示訪問頻率過高,被限制
        
            def wait(self):
                # 還須要等多少秒才能訪問
                ctime = time.time()
                return 60 - (ctime - self.history[-1])

  注意:throttle_classes=[] ,allow_request返回True或False,True表示交給下一個頻率類對象檢查,False表示檢查不經過,終止檢查流程並返回當前的wait函數所返回的提示時間。
  

  •   內置限流類SimpleRateThrottle

  頻率訪問的內置類很是精彩,有一個SimpleRateThrottle經過在配置文件裏面設置相應的頻率,內部便可實現。還有它的wait方法提供了倒數功能

  1. 首先繼承SimpleRateThrottle並定義變量scope做爲頻率配置的key
  2. 而後,覆寫 get_cache_key(self, request, view) ,要返回去字典中取值時候的用戶標識,通常爲ip地址,因此能夠直接返回 self.get_ident(request)
    AnonRateThrottle:用戶未登陸請求限速,經過IP地址判斷
    UserRateThrottle:用戶登錄後請求限速,經過token判斷
    
    from rest_framework.throttling import BaseThrottle, SimpleRateThrottle
    class AnonRateThrottle(SimpleRateThrottle):
        scope = "Anon"
    
        def get_cache_key(self, request, view):
       # 返回查找鍵值
    return self.get_ident(request) class UserRateThrottle(SimpleRateThrottle): scope = "User" def get_cache_key(self, request, view): return request.user.username
  3. settings.py配置
    REST_FRAMEWORK = {
      'DEFAULT_THROTTLE_CLASSES': (
        'rest_framework.throttling.AnonRateThrottle',
        'rest_framework.throttling.UserRateThrottle'
      ),
      'DEFAULT_THROTTLE_RATES': {
        'anon': '100/day',
        'user': '1000/day'
      }
    }
    # DEFAULT_THROTTLE_RATES 包括 second, minute, hour, day

版本處理

通常不須要本身編寫類,只要根據本身版本表示的形式在settings.py配置好便可

目的是能夠在視圖方法中輕鬆地用request.version獲取版本
附加了一個錦上添花的request.versioning_scheme。這貨能夠幫助反向生成當前視圖版本的其餘url
request.versioning_scheme.reverse(viewname='index',request=request)。

版本表示的形式

  • 以GET參數形式(小衆)
    versioning_class = QueryParameterVersioning #注意,這裏不用列表了
  • 在url中傳參(大衆)
    versioning_class = URLPathVersioning
    注意:使用這個須要和url正則配套使用
    urlpatterns = [
    url(r'^(?P<version>[v1|v2]+)/users/$', views.UsersView.as_view()),
    ]

setting.py配置

# 配置容許的版本,版本參數key,和默認的版本
REST_FRAMEWORK = { "DEFAULT_VERSIONING_CLASS":"rest_framework.versioning.URLPathVersioning", "DEFAULT_VERSION":'v1', "ALLOWED_VERSIONS":['v1','v2'], "VERSION_PARAM":'version', }

解析器

通常不須要本身編寫類,只要根據本身版本表示的形式在settings.py配置好便可

解析器的本質是根據請求頭Content-Type的不一樣讓不一樣的解析器去處理
爲的是能夠輕鬆使用request.data(request.body數據的轉換)輕鬆得到傳過來的數據,通常解析完成後爲字典形式

通常爲JSONParser,FormParser

 from rest_framework.parsers import JSONParser,FormParser
    parser_classes = [JSONParser,FormParser,]
#JSONParser:表示只能解析content-type:application/json頭,content-type:application/json頭表示數據是json格式
#FormParser:表示只能解析content-type:application/x-www-form-urlencoded頭

文件上傳解析器

  • MultiPartParser

FileUploadParser只適用於單文件上傳,若要夾帶另外數據則應該是 parser_classes = [FormParser, MultiPartParser]
分別從request.data和request.FILES中得到

  • FileUploadParser

 FileUploadParser只適用於單文件上傳,若要夾帶另外數據則應該是
c. FileUploadParser 對於request.FILES和request.data的數據都是同樣的
若是調用FileUploadParser時,有傳入filename參數,就使用其做爲filename
若是url中沒有設置filename參數,那麼客戶端在請求頭中就必須設置Content-Disposition,例如Content-Disposition: attachment; filename=upload.jpg

a. let headers = {
// 'Content-Type': 'application/x-www-form-urlencoded',
'Content-Disposition': `attachment; filename=${content.file.name}`
}

序列化器

序列化器能夠作數據校驗和queryset的序列化顯示

數據校驗

  • 定義
    普通版
    from rest_framework import serializers
    class UserSerializer(serializers.Serializer):
        id = serializers.IntegerField()
        title = serializers.CharField()
    # 自定義驗證字段,要麼返回原值value,要麼報錯
    def validate_xxx(self,value):
        # from rest_framework import exceptions
        # raise exceptions.ValidationError('字段驗證不經過')
      return value

   ModelSerializer版

class UserInfoSerializer(serializers.ModelSerializer):
     class Meta:
         model = models.UserInfo
         # fields = "__all__"
         fields = ['id','username','password','oooo','rls','group','x1']
         depth = 0 # 0表明只去當前本身層去取,數字越大,就往foreignkey 或 manytomany 取出相應的字典形式數據 
  • 使用
    class UserGroupView(APIView):
    
        def post(self, request, *args, **kwargs):
            ser = UserGroupSerializer(data=request.data)
            if ser.is_valid():
                print(ser.validated_data['title'])
            else:
                print(ser.errors)
            return HttpResponse('提交數據')

序列化處理

  序列化和以前序列化的對比

  

  序列化的字段處理,主要講model裏特殊字段的處理

  • 帶choice參數字段處理

   使用source參數(source參數能夠是指向要序列化對象__dict__裏的任意一個,框架內部會根據source利用反射查找)。
  當存在source參數時,Serializer類變量的變量名就可任意定義了。source參數能夠是函數名,可是該函數不能帶參數,其框架內部用iscallable檢查是否能夠調用,如若能夠,序列化時調用返回

   

  • 外鍵字段Foreign顯示
  1.  外鍵字段也是利用source參數
  2. 值得注意的是序列化字段的是根據最終要序列成的類型而進行定義
    例如:serializers.CharField(source='role.title')  # 外鍵字段的title最終顯示的是字符串(CharField)
  • MamyToMany字段處理
  1. 經過source也是能夠作的,可是作不到特別細的粒度|
    例如:lovers = serializers.CharField(source='lover.all')
  2.  要使用自定義顯示才行
    rls = serializers.SerializerMethodField()  # 自定義顯示
    def get_rls(self, row): 
     #函數名如同form的clean_xx同樣都是配對字段的。get_xx
        role_obj_list = row.roles.all()
        ret = []
        for item in role_obj_list:
        ret.append({'id':item.id,'title':item.title})
        return ret #返回的是字段要顯示的

     3. 還能夠定義本身的Field,覆寫to_representation,該方法返回字段對應的序列化結果
    

    1. 注意:繼承哪一個字段類型都行,由於覆寫to_representation這個方法(輸出最後的結果)
    2. 返回任何類型的結果都行
    3. 參數value是經source參數反射取出的結果
  • 生成hypermedilalink

    1.lookup_field 替代了source來進行反射查找,而且這個參數使用'_'分割
    2.使用時要在serilizer對象初始化時加入context={'request':request}

              url(r'^(?P<version>[v1|v2]+)/group/(?P<xxx>\d+)$', views.GroupView.as_view(),name='gp') #測試url
    class UserInfoSerializer(serializers.ModelSerializer):
                group = serializers.HyperlinkedIdentityField(view_name='gp',lookup_field='group_id',lookup_url_kwarg='xxx')
                class Meta:
                    model = models.UserInfo
                    # fields = "__all__"
                    fields = ['id','username','password','group','roles']
                    #extra_kwargs={'user':{'min_length':6},'password':{'validators':[xxxx,]] #驗證時候添加,xxx是指定的函數對象
                    depth = 0 # 0 ~ 10

     

分頁組件

  • 原生普通分頁
    看第n頁,每頁看多少數據,以及指定對應的GET參數,注意這些GET參數能夠設爲None,表示取消相應的功能
    settings配置對PageNumberPagination有效 
    REST_FRAMEWORK = {
        #分頁
        "PAGE_SIZE":2   #每頁顯示多少個
    }

     固然咱們也能夠本身繼承PageNumberPagination,在類裏面定義

        class MyPageNumberPagination(pagination.PageNumberPagination):
            page_size = 5
            max_page_size = 10
            page_size_query_param = 'size'
            page_query_param = 'page' 
        
            ○ 使用
                def get(self, request, *args, **kwargs):
                    # 測試分頁對象
                    from rest.utils.pagination import MyPageNumberPagination
                    from rest.utils.FormSerializer import RoleFormSerializer
                    roles = models.RoleInfo.objects.all()
            
                    # 建立分頁對象
                    pager = MyPageNumberPagination()
                    # pager = pagination.PageNumberPagination() # 須要在setting裏配置,否則沒數據
                    # 在數據庫中獲取分頁的數據
                    data_list = pager.paginate_queryset(queryset=roles, request=request, view=self)
                    # data_list 已是roleinfo對象列表,接下來序列化
                    ser = RoleFormSerializer(instance=data_list, many=True)
                    return Response(ser.data)

     

 

  •   偏移分頁
      指定位置,看這個位置的後的第n條數據,以及指定的對應的GET參數
        #繼承主要是改變max_limit最大大小
            class MyOffsetPagination(pagination.LimitOffsetPagination):
                default_limit = 2  # 默認每頁大小
                max_limit = 5  # 人工指定的最大大小
                limit_query_param = 'limit'
                offset_query_param = 'offset'
            
            a. 使用
                 def get(self, request, *args, **kwargs):
                          roles = models.RoleInfo.objects.all()
                        pager = MyOffsetPagination()
                        data_list = pager.paginate_queryset(queryset=roles, request=request, view=self)
                        ser = RoleFormSerializer(instance=data_list, many=True)
                        return Response(ser.data)

     

  • 遊標分頁
    加密分頁,只有上一頁和下一頁功能

        # 特殊的是要設置ordering和返回時是用專用的方法
        class MyCursorPagination(pagination.CursorPagination):
            ordering = '-id'  # 必定要設置好排序規則
            page_size = 5
            max_page_size = 10
            page_size_query_param = 'size'
            cursor_query_param = 'cursor'
        
        • 使用
         from rest.utils.pagination import MyCursorPagination
                pager = MyCursorPagination()
                data_list = pager.paginate_queryset(queryset=roles, request=request, view=self)
                ser = RoleFormSerializer(instance=data_list, many=True)
                return pager.get_paginated_response(ser.data)  # 使用遊標分頁特殊的方法返回

視圖組件

複雜邏輯 GenericViewSet 或 APIView
增刪改查:ModelViewset
增刪 CreateModelMixin,DestroyModelMixin,GenericViewSet

 

  • GenericViewSet

  改變請求方式對應的調用函數,要經過對as_view({'get':'list'','post':'add'})傳參
  注意:但凡支持{‘get’:’list’}這種as_view傳參,就表明其繼承了ViewSetMixin。因此class MyView(ViewSetMixin,APIView) 替代GenericViewSet
  改變請求方式調用的視圖函數這個在多個url對應一個CBV時有奇效。

  • mixins.ListModelMixin

  增刪改查組件,以多繼承的方式使用
  如 mixins.ListModelMixin裏面有一個list函數,它提供了像上面同樣的用法,即顯示一個對象集合

    from rest_framework import mixins
    class RolesView3(viewsets.GenericViewSet, mixins.ListModelMixin):
        queryset = models.RoleInfo.objects.all()
        serializer_class = RoleFormSerializer
       pagination_class = MyPageNumberPagination

 

  •   ModelViewSet

  繼承了GenericViewSet,並集齊了增刪改查,局部更新組件
  注意:url必定要有一個命名正則pk

    url(r'^(?P<version>[v1|v2]+)/role/(?P<pk>\d+)/$',RoleView.as_view({'get': 'retrieve', 'delete': 'destroy', 'put': 'update', 'patch': 'partial_update'}),name='role'),

  因爲url的增長和查看,因此通常定義兩個url,一個針對象集合,一個針對單一對象的操做

    #視圖只需一個,url兩個
    class RoleView(viewsets.ModelViewSet):
        queryset = models.RoleInfo.objects.all() # 直接傳入全局對象,框架內部會根據傳入的pk返回單一對象
        serializer_class = RoleFormSerializer
        pagination_class = MyPageNumberPagination

路由組件

  當出現這麼一個需求:在url後面.json,使得渲染器顯示原生的的json數據
  咱們的解決方案是,有定義兩條url配合渲染器達到效果

 url(r'^(?P<version>[v1|v2]+)/roles4/$',RolesView4.as_view({'get': 'list', 'post': 'create'}), name='roles4'),
  url(r'^(?P<version>[v1|v2]+)/roles4\.(?P<format>\w+)$', RolesView4.as_view({'get': 'list', 'post': 'create'}), name='roles4'),

  或者定義一條url達到效果

 url(r'^(?P<version>[v1|v2]+)/roles4(\.(?P<format>\w+))?$',RolesView4.as_view({'get': 'list', 'post': 'create'}), name='roles4'),

 

  路由組件提供全自動路由
  所謂全自動,指不用再as_view指定組件的指定函數名稱,並且還生成了.json後綴,自動生成單一對象和對象集合的4組url
  使用場景:單一個api對一個表進行簡單的增刪改查(配合ModelViewset)用得多,可是單單的增刪功能就沒有必要了

# 如下寫在url文件裏
from rest_framework import routers
router = routers.DefaultRouter()
router.register(r'xxxx', views.RouterView)  # xxxx表明url標誌符
urlpatterns = [ url(r'^(?P<version>[v1|v2]+)/', include(router.urls)), #注意: xxxx表明url標誌符,在指定前綴正則('^(?P<version>[v1|v2]+)/)後面添加 # http://127.0.0.1:8000/api/v1/xxx/

 

渲染器

通常配置在settings.py裏,目的是配置瀏覽器如何顯示json(爲了頁面更好看)

from rest_framework import renderers
renderer_classes = [renderers.JSONRenderer, renderers.BrowsableAPIRenderer, renderers.AdminRenderer]
# 例如使用 admin渲染器:http://127.0.0.1:8000/api/v1/roles4/?format=admin


注:能夠自定義顯示頁面
將BrowsableAPIRenderer的template變量指向的模版文件改掉就好了

 

最後

相關文章
相關標籤/搜索