Django restfulframework 開發相關知識 整理


 

目錄

前言

一句話概括RESTful
一種設計風格,通過URL定位資源,HTTP動詞描述操作。旨在讓我們更好更快地開發出web應用

前后端分離

前后端分離帶來的優點

  • 可以為多個不同客戶端提供同一個數據源
    不同類型的客戶端(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網站請求數據的接口

  • 子域名方式

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

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

  • url方式

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

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

版本表示規范

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

url使用名詞

  通過url定位資源

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

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

 

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變量指向的模版文件改掉就行了

 

最后


免責聲明!

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



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