1. 程式人生 > 實用技巧 >drf入門規範

drf入門規範

一. Web應用模式#

在開發Web應用中,有兩種應用模式:

知識儲備: 什麼是動態頁面(查資料庫的),什麼是靜態頁面(靜止的html)

Copy
# 判斷條件: 根據html頁面內容是寫死的還是從後端動態獲取的
靜態網頁: 頁面上的資料是直接寫死的 萬年不變
動態網頁: 資料是實時獲取的. 如下例子: 
    1.後端獲取當前時間展示到html頁面上
    2.資料是從資料庫中獲取的展示到html頁面上
    
# 總結
靜: 頁面資料寫死的
動: 查資料庫

1. 前後端不分離#

前後端混合開發(前後端不分離):返回的是html的內容,需要寫模板

關鍵: 請求動態頁面, 返回HTML

2. 前後端分離#

前後端分離:只專注於寫後端介面,返回json,xml格式資料

關鍵: 到靜態檔案伺服器請求靜態頁面, 靜態檔案伺服器返回靜態頁面. 然後JS請求Django後端, Django後端返回json或者XMl格式的資料

Copy
# xml格式
<xml>
<name>lqz</name>
</xml>

# json格式
{"name":"lqz"}

# asp 動態伺服器頁面. jsp Java服務端網頁
# java---> jsp
https://www.pearvideo.com/category_loading.jsp
#php寫的
http://www.aa7a.cn/user.php # python寫的 http://www.aa7a.cn/user.html # 動靜態頁面存在的主要作用 優化查詢

3. 總結#

Copy
# 動靜態頁面
    靜態: 頁面內容寫死的, 內容都是固定不變的.
    動態: 頁面內容含有需要從資料庫中獲取的.
        
# 前後端不分離
	特點: 請求動態頁面, 返回HTML資料或者重定向
        
# 前後端分離特點
	特點: 
        請求靜態頁面(向靜態伺服器), 返回靜態檔案
       	請求需要填充的資料, 返回js或者xml格式資料

二. API介面#

為了在團隊內部形成共識、防止個人習慣差異引起的混亂,我們需要找到一種大家都覺得很好的介面實現規範,而且這種規範能夠讓後端寫的介面,用途一目瞭然,減少雙方之間的合作成本。

通過網路,規定了前後臺資訊互動規則的url連結,也就是前後臺資訊互動的媒介

Web API介面和一般的url連結還是有區別的,Web API介面簡單概括有下面四大特點

  • url:長得像返回資料的url連結

  • 請求方式:get、post、put、patch、delete

    • 採用get方式請求上方介面
  • 請求引數:json或xml格式的key-value型別資料

    • ak:6E823f587c95f0148c19993539b99295
    • region:上海
    • query:肯德基
    • output:json
  • 響應結果:json或xml格式的資料

    • 上方請求引數的output引數值決定了響應資料的格式
Copy
# xml格式
https://api.map.baidu.com/place/v2/search?ak=6E823f587c95f0148c19993539b99295&region=%E4%B8%8A%E6%B5%B7&query=%E8%82%AF%E5%BE%B7%E5%9F%BA&output=xml
    
#json格式
https://api.map.baidu.com/place/v2/search?ak=6E823f587c95f0148c19993539b99295&region=%E4%B8%8A%E6%B5%B7&query=%E8%82%AF%E5%BE%B7%E5%9F%BA&output=json
{
    "status":0,
  	"message":"ok",
    "results":[
        {
            "name":"肯德基(羅餐廳)",
            "location":{
                "lat":31.415354,
                "lng":121.357339
            },
            "address":"月羅路2380號",
            "province":"上海市",
            "city":"上海市",
            "area":"寶山區",
            "street_id":"339ed41ae1d6dc320a5cb37c",
            "telephone":"(021)56761006",
            "detail":1,
            "uid":"339ed41ae1d6dc320a5cb37c"
        }
      	...
		]
}

總結#

Copy
什麼是API介面?
	API介面就是前後端資訊互動的媒介(提示: 表示的是前後端之間)

三. 介面測試工具:Postman#

Postman是一款介面除錯工具,是一款免費的視覺化軟體,同時支援各種作業系統平臺,是測試介面的首選工具。

Postman可以直接從官網:https://www.getpostman.com/downloads/下載獲得,然後進行傻瓜式安裝。

  • 工作面板

  • 簡易的get請求

  • 簡易的post請求

  • 案例:請求百度地圖介面

四. RESTful API規範#

REST全稱是Representational State Transfer,中文意思是表述(編者注:通常譯為表徵性狀態轉移)。 它首次出現在2000年Roy Fielding的博士論文中。

RESTful是一種定義Web API介面的設計風格,尤其適用於前後端分離的應用模式中。

這種風格的理念認為後端開發任務就是提供資料的,對外提供的是資料資源的訪問介面,所以在定義介面時,客戶端訪問的URL路徑就表示這種要操作的資料資源。

事實上,我們可以使用任何一個框架都可以實現符合restful規範的API介面。

1. 資料的安全保障#

Copy
# url連結一般都採用https協議進行傳輸

# 注:採用https協議,可以提高資料互動過程中的安全性

2. 介面特徵表現#

Copy
# 用api關鍵字標識介面url:
	[https://api.baidu.com](https://api.baidu.com/)
	https://www.baidu.com/api
       
# 注:看到api字眼,就代表該請求url連結是完成前後臺數據互動的

3. 多資料版本共存#

Copy
# 在url連結中標識資料版本
	https://api.baidu.com/v1
	https://api.baidu.com/v2

# 注:url連結中的v1、v2就是不同資料版本的體現(只有在一種資料資源有多版本情況下)

4. 資料即是資源,均使用名詞(可複數)#

Copy
# 介面一般都是完成前後臺數據的互動,互動的資料我們稱之為資源
	https://api.baidu.com/users
	https://api.baidu.com/books
	https://api.baidu.com/book

# 注:不要出現操作資源的動詞,錯誤示範:https://api.baidu.com/delete-user

# 例外: 特殊的介面可以出現動詞,因為這些介面一般沒有一個明確的資源,或是動詞就是介面的核心含義
	https://api.baidu.com/place/search
	https://api.baidu.com/login

5. 資源操作由請求方式決定(method)#

Copy
# 操作資源一般都會涉及到增刪改查,我們提供請求方式來標識增刪改查動作
    https://api.baidu.com/books   - get請求:   獲取所有書
    https://api.baidu.com/books/1 - get請求:   獲取主鍵為1的書
    https://api.baidu.com/books   - post請求:  新增一本書
    https://api.baidu.com/books/1 - put請求:   整體修改主鍵為1的書
    https://api.baidu.com/books/1 - patch請求: 區域性修改主鍵為1的書
    https://api.baidu.com/books/1 - delete請求:刪除主鍵為1的書

6. 過濾,通過在url上傳參的形式傳遞搜尋條件#

Copy
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        指定篩選條件

7. 響應狀態碼#

Copy
# 正常響應
    響應狀態碼2xx
    200:常規請求
    201:建立成功

# 重定向響應
	響應狀態碼3xx
    301:永久重定向
    302:暫時重定向

# 客戶端異常
    響應狀態碼4xx
    403:請求無許可權
    404:請求路徑不存在
    405:請求方法不存在

# 伺服器異常
    響應狀態碼5xx
    500:伺服器異常

8. 錯誤處理,應返回錯誤資訊,error當做key#

Copy
{
    error: "無許可權操作"
}

9. 返回結果,針對不同操作,伺服器向用戶返回的結果應該符合以下規範#

Copy
GET /collection             返回資源物件的列表(陣列)  多個[{}],
GET /collection/resource    返回單個資源物件           單個{}
POST /collection            返回新生成的資源物件
PUT /collection/resource    返回完整的資源物件
PATCH /collection/resource  返回完整的資源物件
DELETE /collection/resource 返回一個空文件

10. 需要url請求的資源需要訪問資源的請求連結#

Copy
# Hypermedia API,RESTful API最好做到Hypermedia,即返回結果中提供連結,連向其他API方法,使得使用者不查文件,也知道下一步應該做什麼
{
  	"status": 0,
  	"msg": "ok",
  	"results":[
        {
            "name":"肯德基(羅餐廳)",
            "img": "https://image.baidu.com/kfc/001.png"
        }
      	...
		]
}

比較好的介面返回

Copy
# 響應資料要有狀態碼、狀態資訊以及資料本身
{
  	"status": 0,
  	"msg": "ok",
  	"results":[
        {
            "name":"肯德基(羅餐廳)",
            "location":{
                "lat":31.415354,
                "lng":121.357339
            },
            "address":"月羅路2380號",
            "province":"上海市",
            "city":"上海市",
            "area":"寶山區",
            "street_id":"339ed41ae1d6dc320a5cb37c",
            "telephone":"(021)56761006",
            "detail":1,
            "uid":"339ed41ae1d6dc320a5cb37c"
        }
      	...
		]
}

11. 總結#

Copy
# 關鍵字: HTTPS協議, api, 版本, 資源標識, 請求方式, url傳參標識, 響應狀態碼, 錯誤資訊, 不同的操作返回不同的結果, 返回url

1. 傳輸資料用HTTPS協議

2. 介面具有標識性
    https://api.baidu.com
    https://www.baidu.com/api
        
3. 介面具有版本標識性
    https://api.baidu.com/v1
    https://api.baidu.com/v2
        
4. 核心: 介面對資源具有標識性(名詞)
    https://api.baidu.com/books
    https://api.baidu.com/book
        
5. 核心: 通過請求的方式來決定對資料的操作方式
    get獲取, post增加, put整體更新, patch區域性更新, delete刪除
    
6. 通過url傳引數的形式傳遞搜尋條件
    https://api.baidu.com/books?limit=10
        
7. 響應狀態碼
    200常規請求   201建立成功請求
    301永久重定向 302暫時重定向
    403請求無許可權 404請求無路徑   405請求方法不存在
    500服務端異常
    
8. 錯誤資訊
    {
        'error': '無許可權操作'
    }
    
9. 針對不同的操作, 返回不同的返回結果
    get     獲取多個[{}], 獲取單個{}
    post    返回新增的
    put     返回修改後所有的內容(包括沒修改的. 全部)
    patch   返回修改後所有的內容(包括沒修改的. 全部)
    delete  返回空文件
    
10. 基於請求響應過後返回內容中, 可以帶url地址
    {
        "status": 0,
        "msg": "ok",
        "results":[
            {
                "name":"肯德基(羅餐廳)",
                "img": "https://image.baidu.com/kfc/001.png"
             }
        ...
        ]
    }

五. 序列化#

api介面開發,最核心最常見的一個過程就是序列化,所謂序列化就是把資料轉換格式,序列化可以分兩個階段:

序列化: 把我們識別的資料轉換成指定的格式提供給別人。

例如:我們在django中獲取到的資料預設是模型物件,但是模型物件資料無法直接提供給前端或別的平臺使用,所以我們需要把資料進行序列化,變成字串或者json資料,提供給別人。

反序列化:把別人提供的資料轉換/還原成我們需要的格式。

例如:前端js提供過來的json資料,對於python而言就是字串,我們需要進行反序列化換成模型類物件,這樣我們才能把資料儲存到資料庫中。

六. Django Rest_Framework#

核心思想: 縮減編寫api介面的程式碼

Django REST framework是一個建立在Django基礎之上的Web 應用開發框架,可以快速的開發REST API介面應用。在REST framework中,提供了序列化器Serialzier的定義,可以幫助我們簡化序列化與反序列化的過程,不僅如此,還提供豐富的類檢視、擴充套件類、檢視集來簡化檢視的編寫工作。REST framework還提供了認證、許可權、限流、過濾、分頁、介面文件等功能支援。REST framework提供了一個API 的Web視覺化介面來方便檢視測試介面。

官方文件:https://www.django-rest-framework.org/

github: https://github.com/encode/django-rest-framework/tree/master

特點#

Copy
提供了定義序列化器Serializer的方法,可以快速根據 Django ORM 或者其它庫自動序列化/反序列化;

提供了豐富的類檢視、Mixin擴充套件類,簡化檢視的編寫;

豐富的定製層級:函式檢視、類檢視、檢視集合到自動生成 API,滿足各種需要;

多種身份認證和許可權認證方式的支援;[jwt]

內建了限流系統;

直觀的 API web 介面;

可擴充套件性,外掛豐富

七. drf的安裝和簡單使用#

Copy
# 安裝:pip install djangorestframework==3.10.3

# 使用
# 1. 在setting.py 的app中註冊
    INSTALLED_APPS = [
    'rest_framework'
    ]
    
# 2. 在models.py中寫表模型
    class Book(models.Model):
        nid=models.AutoField(primary_key=True)
        name=models.CharField(max_length=32)
        price=models.DecimalField(max_digits=5,decimal_places=2)
        author=models.CharField(max_length=32)
        
# 3. 新建一個序列化類
    from rest_framework.serializers import ModelSerializer
    from app01.models import  Book
    class BookModelSerializer(ModelSerializer):
        class Meta:
            model = Book
            fields = "__all__"
            
# 4. 在檢視中寫檢視類
    from rest_framework.viewsets import ModelViewSet
    from .models import Book
    from .ser import BookModelSerializer
    class BooksViewSet(ModelViewSet):
        queryset = Book.objects.all()
        serializer_class = BookModelSerializer
        
# 5. 寫路由關係
    from app01 import views
    from rest_framework.routers import DefaultRouter
    router = DefaultRouter()  # 可以處理檢視的路由器
    router.register('book', views.BooksViewSet)  # 向路由器中註冊檢視集
      # 將路由器中的所以路由資訊追到到django的路由列表中
    urlpatterns = [
        path('admin/', admin.site.urls),
    ]
    #這是什麼意思?兩個列表相加
    # router.urls  列表
    urlpatterns += router.urls
    
# 6. 啟動,在postman中測試即可

八. CBV中對繼承的View原始碼分析#

1. 程式碼分析#

切入點: urls.py中的.as_view()方法

Copy
path('books1/', views.Books.as_view())

檢視中Books類

Copy
class Books(View):
    # 如果有個需求,只能接受get請求
    http_method_names = ['get', ]

    def get(self, request):
        print(self.request)   # 看: 這裡可以通過self.request進行獲取到request方法
        return HttpResponse('ok')

專案啟動時執行

Copy
@classonlymethod
def as_view(cls, **initkwargs):
	...

    def view(request, *args, **kwargs):
        ...

    ...
    return view

路由匹配時執行, 將執行的類進行例項化, 通過執行路由匹配中類例項化出的物件就可以執行到繼承父類View中定義的dispatch方法

Copy
def view(request, *args, **kwargs):
        self = cls(**initkwargs)
        if hasattr(self, 'get') and not hasattr(self, 'head'):
            self.head = self.get
        self.request = request
        self.args = args
        self.kwargs = kwargs
        return self.dispatch(request, *args, **kwargs)

通過dispatch方法中使用反射獲取請求的型別去執行對應類中定義的請求的方法

Copy
def dispatch(self, request, *args, **kwargs):
    # Try to dispatch to the right method; if a method doesn't exist,
    # defer to the error handler. Also defer to the error handler if the
    # request method isn't on the approved list.
    if request.method.lower() in self.http_method_names:
        handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
    else:
        handler = self.http_method_not_allowed
    return handler(request, *args, **kwargs)

上面的self.http_method_names判斷, 如果子類中沒有派生那麼就會通過View中定義的進行判斷

Copy
class View(object):
    http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
    ...

2. 圖示分析#

第一步: 專案啟動: views.Books.as_view()就是View類中的view函式記憶體地址

第二步: 路由匹配: 將view加括號呼叫, 並傳入request物件.

第三步: 執行view, 將Books類進行例項化, 例項化出Books類的物件. 這裡的self就是Books類例項化出的物件, 執行self.dispatch方法. self就是Books類例項化出的物件, 如果子類以及物件中都沒有定義, 就會執行View類中的dispatch方法, dispatch方法中就是通過使用者當前的請求的小寫字串進行反射. 當繼承View的類中有get方法, 就會參照物件屬性查詢順序就會呼叫子類中定義的get方法.

3. 流程總結#

Copy
1. 專案啟動: views.Books.as_view()就是View類中的view函式記憶體地址
2. 路由匹配: 將view加括號呼叫, 並傳入request物件. 
3. 執行view: 將Books類進行例項化, 例項化出Books類的物件. 這裡的self就是Books類例項化出的物件
4. 執行self.dispatch方法: self就是Books類例項化出的物件, 如果之類以及物件中都沒有定義, 就會執行View類中的dispatch方法
5. dispatch方法中就是通過使用者當前的請求的小寫字串進行反射. 當繼承View的類中有get方法, 根據物件屬性查詢順序就會呼叫子類中定義的get方法.

九. CBV中對繼承的APIView原始碼分析#

1. 圖示分析#

專案啟動: BooksAPIView就是View類中的view函式的記憶體地址. 只不過由APIView進行了派生, 再次self.cls=cls等包裝加工以後返回的.

路由匹配加括號呼叫view(request)執行:

第一步: request包裝. 在執行BooksAPIView中定義的請求方法之前, 先做了request的包裝, BooksAPIView中獲取的request不再是原生髮過來的request物件了, 如果想要獲取原生的request物件我們可以通過self.request._request 或者 request.request即可.

第二步: drf提供的三段認證元件. 執行完request包裝以後執行了三段認證self.initial(…).

第三步: 執行BookAPiView類中定義的方法

第四步: **response沒有立即做了處理, 而是判斷返回的渲染模式. ** 舉例: 如果是postman訪問, 那麼就返回的是json格式. 如果是瀏覽器就會返回好看的頁面, 這個好看的頁面可以根據不同的請求方式對應對資料的操作方法.

三段請求認證元件: self.initial(…)

Copy
# APIView的initial方法
 	def initial(self, request, *args, **kwargs):
        # 認證元件:校驗使用者 - 遊客、合法使用者、非法使用者
        # 遊客:代表校驗通過,直接進入下一步校驗(許可權校驗)
        # 合法使用者:代表校驗通過,將使用者儲存在request.user中,再進入下一步校驗(許可權校驗)
        # 非法使用者:代表校驗失敗,丟擲異常,返回403許可權異常結果
        self.perform_authentication(request)
        # 許可權元件:校驗使用者許可權 - 必須登入、所有使用者、登入讀寫遊客只讀、自定義使用者角色
        # 認證通過:可以進入下一步校驗(頻率認證)
        # 認證失敗:丟擲異常,返回403許可權異常結果
        self.check_permissions(request)
        # 頻率元件:限制檢視介面被訪問的頻率次數 - 限制的條件(IP、id、唯一鍵)、頻率週期時間(s、m、h)、頻率的次數(3/s)
        # 沒有達到限次:正常訪問介面
        # 達到限次:限制時間內不能訪問,限制時間達到後,可以重新訪問
        self.check_throttles(request)

繼承APIView檢視的類中含有的方法分析:

2. 流程總結#

Copy
1. 專案啟動時: views.BooksAPIView.as_view()等於View中定義as_view方法中view函式. 只不過是通過APIView派生賦值了一些屬性以後返回的
2. 路由匹配時: 在執行檢視類中定義的方法之前. 先將request進行的包裝, 在執行了三段認證組元件, 最後才是執行了檢視類中定義的不同請求的方法
3. 檢視返回時: 先對返回的response進行了處理. 再返回的渲染結果 或者 json格式資料

十. 拓展#

1. 一切皆物件, 函式也是物件#

Copy
def foo(a, b):
    return a + b


foo.name = 'lqz'  # 由於一切皆物件,函式也是個物件,物件放值

print(foo(2, 3))

print(foo.name)  # lqz

2. 區域性禁用csrf方法csrf_exempt#

Copy
# 提示: 在檢視函式上加裝飾器@csrf_exempt. 與csrf_exempt(view)這麼寫和在檢視函式上加裝飾器是一模一樣的.

# urls.py中這種寫法本質就是返回的views.test, 只是在原來基礎之上不修改的呼叫方式, 以及原始碼的情況下, 去除了csrf認證
path('test/', csrf_exempt(views.test)),