日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

引入DjangoRESTframework

作者:大托爾 更新時間: 2022-09-25 編程語言

引入DjangoRESTframework

  • web應用模式
    • 1 前后端不分離
    • 2 前后端分離
  • 認識RESTful
  • RESTful設計方法
    • 1. 域名
    • 2. 版本(Versioning)
    • 3. 路徑(Endpoint)
    • 3. HTTP動詞
    • 4. 過濾信息(Filtering)
    • 6. 狀態碼(Status Codes)
    • 7. 錯誤處理(Error handling)
    • 8. 返回結果
    • 9. 其他
  • 使用Django開發REST接口
    • 測試
  • 明確REST接口開發的核心任務
    • 序列化Serialization
    • 總結
  • DjangoRESTframework簡介
    • 認識Django REST framework
    • 特點
  • 筆記

在本章中,我們要大家介紹為什么學習Django REST framework,它能幫助我們做哪些事情。
課程思路:
我們從分析現在流行的前后端分離Web應用模式說起,然后介紹如何設計REST API,通過使用Django來實現一個REST API為例,明確后端開發REST API要做的最核心工作,然后介紹Django REST framework能幫助我們簡化開發REST API的工作。

web應用模式

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

  • 前后端不分離
  • 前后端分離

1 前后端不分離

在這里插入圖片描述
在前后端不分離的應用模式中,前端頁面看到的效果都是由后端控制,由后端渲染頁面或重定向,也就是后端需要控制前端的展示,前端與后端的耦合度很高。

這種應用模式比較適合純網頁應用,但是當后端對接App時,App可能并不需要后端返回一個HTML網頁,而僅僅是數據本身,所以后端原本返回網頁的接口不再適用于前端App應用,為了對接App后端還需再開發一套接口。

2 前后端分離

在這里插入圖片描述
前后端不分離,兩個服務器
在前后端分離的應用模式中,后端僅返回前端所需的數據,不再渲染HTML頁面,不再控制前端的效果。至于前端用戶看到什么效果,從后端請求的數據如何加載到前端中,都由前端自己決定,網頁有網頁的處理方式,App有App的處理方式,但無論哪種前端,所需的數據基本相同,后端僅需開發一套邏輯對外提供數據即可。

在前后端分離的應用模式中 ,前端與后端的耦合度相對較低。

在前后端分離的應用模式中,我們通常將后端開發的每個視圖都稱為一個接口,或者API,前端通過訪問接口來對數據進行增刪改查。

前后端分類,js渲染,兩個服務器,后端不用編寫任何前端代碼
前后端不分離,模板渲染,一個服務器,后端可能要編寫前端代碼

認識RESTful

在前后端分離的應用模式里,API接口如何定義?

例如對于后端數據庫中保存了商品的信息,前端可能需要對商品數據進行增刪改查,那相應的每個操作后端都需要提供一個API接口:

  1. POST /add-goods 增加商品
  2. POST /delete-goods 刪除商品
  3. POST /update-goods 修改商品
  4. GET /get-goods 查詢商品信息
    對于接口的請求方式與路徑,每個后端開發人員可能都有自己的定義方式,風格迥異。

是否存在一種統一的定義方式,被廣大開發人員接受認可的方式呢?

這就是被普遍采用的API的RESTful設計風格。

RESTful設計方法

RESTful 用于規范接口的設計,來使廣大人員都可以接受

1. 域名

應該盡量將一類處理類似數據的API部署在專用域名之下。

比如說對手機操作的api 可以設置一個 shouji.jd.com 域名

https://api.example.com

如果確定API很簡單,不會有進一步擴展,可以考慮放在主域名下。

https://example.org/api/

2. 版本(Versioning)

應該將API的版本號放入URL。

http://www.example.com/app/1.0/foo

http://www.example.com/app/1.1/foo

http://www.example.com/app/2.0/foo

另一種做法是,將版本號放在HTTP頭信息中,但不如放入URL方便和直觀。Github采用這種做法。

因為不同的版本,可以理解成同一種資源的不同表現形式,所以應該采用同一個URL。版本號可以在HTTP請求頭信息的Accept字段中進行區分(參見Versioning REST Services):

Accept: vnd.example-com.foo+json; version=1.0

Accept: vnd.example-com.foo+json; version=1.1

Accept: vnd.example-com.foo+json; version=2.0

3. 路徑(Endpoint)

路徑又稱"終點"(endpoint),表示API的具體網址,每個網址代表一種資源(resource)

(1) 資源作為網址,只能有名詞,不能有動詞,而且所用的名詞往往與數據庫的表名對應。

舉例來說,以下是不好的例子:

/getProducts
/listOrders
/retreiveClientByOrder?orderId=1

對于一個簡潔結構,你應該始終用名詞。 此外,利用的HTTP方法可以分離網址中的資源名稱的操作。

GET /products :將返回所有產品清單
POST /products :將產品新建到集合
GET /products/4 :將獲取產品 4
PATCH(或)PUT /products/4 :將更新產品 4

(2) API中的名詞應該使用復數。無論子資源或者所有資源。

舉例來說,獲取產品的API可以這樣定義

獲取單個產品:http://127.0.0.1:8080/AppName/rest/products/1
獲取所有產品: http://127.0.0.1:8080/AppName/rest/products

最后的數字表示操作數據的id

3. HTTP動詞

對于資源的具體操作類型,由HTTP動詞表示。

常用的HTTP動詞有下面四個(括號里是對應的SQL命令)。

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

還有三個不常用的HTTP動詞。

  • PATCH(UPDATE):在服務器更新(更新)資源(客戶端提供改變的屬性)。
  • HEAD:獲取資源的元數據。
  • OPTIONS:獲取信息,關于資源的哪些屬性是客戶端可以改變的。

下面是一些例子。

GET /zoos:列出所有動物園
POST /zoos:新建一個動物園(上傳文件)
GET /zoos/ID:獲取某個指定動物園的信息
PUT /zoos/ID:更新某個指定動物園的信息(提供該動物園的全部信息)
PATCH /zoos/ID:更新某個指定動物園的信息(提供該動物園的部分信息)
DELETE /zoos/ID:刪除某個動物園
GET /zoos/ID/animals:列出某個指定動物園的所有動物
DELETE /zoos/ID/animals/ID:刪除某個指定動物園的指定動物

動物園的指定動物

4. 過濾信息(Filtering)

如果記錄數量很多,服務器不可能都將它們返回給用戶。API應該提供參數,過濾返回結果。

下面是一些常見的參數。

?limit=10:指定返回記錄的數量
?offset=10:指定返回記錄的開始位置。
?page=2&per_page=100:指定第幾頁,以及每頁的記錄數。
?sortby=name&order=asc:指定返回結果按照哪個屬性排序,以及排序順序。
?animal_type_id=1:指定篩選條件

參數的設計允許存在冗余,即允許API路徑和URL參數偶爾有重復。比如,GET /zoos/ID/animals 與 GET /animals?zoo_id=ID 的含義是相同的。

6. 狀態碼(Status Codes)

服務器向用戶返回的狀態碼和提示信息,常見的有以下一些(方括號中是該狀態碼對應的HTTP動詞)。

  • 200 OK - [GET]:服務器成功返回用戶請求的數據
  • 201 CREATED - [POST/PUT/PATCH]:用戶新建或修改數據成功。
  • 202 Accepted - [*]:表示一個請求已經進入后臺排隊(異步任務)
  • 204 NO CONTENT - [DELETE]:用戶刪除數據成功。
  • 更多情況直接返回200
  • 400 INVALID REQUEST - [POST/PUT/PATCH]:用戶發出的請求有錯誤,服務器沒有進行新建或修改數據的操作
  • 401 Unauthorized - [*]:表示用戶沒有權限(令牌、用戶名、密碼錯誤)。
  • 403 Forbidden - [*] 表示用戶得到授權(與401錯誤相對),但是訪問是被禁止的。
  • 404 NOT FOUND - [*]:用戶發出的請求針對的是不存在的記錄,服務器沒有進行操作,該操作是冪等的。
  • 406 Not Acceptable - [GET]:用戶請求的格式不可得(比如用戶請求JSON格式,但是只有XML格式)。
  • 410 Gone -[GET]:用戶請求的資源被永久刪除,且不會再得到的。
  • 422 Unprocesable entity - [POST/PUT/PATCH] 當創建一個對象時,發生一個驗證錯誤。
  • 500 INTERNAL SERVER ERROR - [*]:服務器發生錯誤,用戶將無法判斷發出的請求是否成功。
    狀態碼的完全列表參見這里或這里。

記住 200 401 403 404 500

7. 錯誤處理(Error handling)

如果狀態碼是4xx,服務器就應該向用戶返回出錯信息。一般來說,返回的信息中將error作為鍵名,出錯信息作為鍵值即可。

{
    error: "Invalid API key"
}

8. 返回結果

針對不同操作,服務器向用戶返回的結果應該符合以下規范。

  • GET /collection:返回資源對象的列表(數組)
  • GET /collection/resource:返回單個資源對象
  • POST /collection:返回新生成的資源對象
  • PUT /collection/resource:返回完整的資源對象
  • PATCH /collection/resource:返回完整的資源對象
  • DELETE /collection/resource:返回一個空文檔

9. 其他

服務器返回的數據格式,應該盡量使用JSON,避免使用XML。

使用Django開發REST接口

在這里插入圖片描述
前端人員可以通過這個寫ajax請求

我們以在Django框架中使用的圖書英雄案例來寫一套支持圖書數據增刪改查的REST API接口,來理解REST API的開發。
在這里插入圖片描述

在這里插入圖片描述
在這可以快速進行數據導入

在此案例中,前后端均發送JSON格式數據。
在這里插入圖片描述
新增數據的時候,傳遞的參數根據模型類設計,沒有默認值,沒有允許為空一般就是必傳的字段
在這里插入圖片描述
在這里插入圖片描述

最外層是列表的,想返回json數據的,要添加:JsonResponse(book_list, safe=False)
在這里插入圖片描述
status為返回的狀態碼
在這里插入圖片描述
返回創建后的結果
在這里插入圖片描述
發送json數據一定要使用雙引號
在這里插入圖片描述
發送的時候會出現csrf認證失敗,先注銷掉csrf機制

# views.py

from datetime import datetime

class BooksAPIVIew(View):
    """
    查詢所有圖書、增加圖書
    """
    def get(self, request):
        """
        查詢所有圖書
        路由:GET /books/
        """
        queryset = BookInfo.objects.all()
        book_list = []
        for book in queryset:
            book_list.append({
                'id': book.id,
                'btitle': book.btitle,
                'bpub_date': book.bpub_date,
                'bread': book.bread,
                'bcomment': book.bcomment,
                'image': book.image.url if book.image else ''
            })
        return JsonResponse(book_list, safe=False)

    def post(self, request):
        """
        新增圖書
        路由:POST /books/ 
        """
        json_bytes = request.body
        json_str = json_bytes.decode()
        book_dict = json.loads(json_str)

        # 此處詳細的校驗參數省略

        book = BookInfo.objects.create(
            btitle=book_dict.get('btitle'),
            bpub_date=datetime.strptime(book_dict.get('bpub_date'), '%Y-%m-%d').date()
        )

        return JsonResponse({
            'id': book.id,
            'btitle': book.btitle,
            'bpub_date': book.bpub_date,
            'bread': book.bread,
            'bcomment': book.bcomment,
            'image': book.image.url if book.image else ''
        }, status=201)


class BookAPIView(View):
    def get(self, request, pk):
        """
        獲取單個圖書信息
        路由: GET  /books/<pk>/
        """
        try:
            book = BookInfo.objects.get(pk=pk)
        except BookInfo.DoesNotExist:
            return HttpResponse(status=404)

        return JsonResponse({
        # 此段代碼直接進行復制
            'id': book.id,
            'btitle': book.btitle,
            'bpub_date': book.bpub_date,
            'bread': book.bread,
            'bcomment': book.bcomment,
            'image': book.image.url if book.image else ''
        })

    def put(self, request, pk):
        """
        修改圖書信息
        路由: PUT  /books/<pk>
        """
        # 更新數據的代碼
        try:
            book = BookInfo.objects.get(pk=pk)
        except BookInfo.DoesNotExist:
            return HttpResponse(status=404)

        json_bytes = request.body
        json_str = json_bytes.decode()
        book_dict = json.loads(json_str)

        # 此處詳細的校驗參數省略

        book.btitle = book_dict.get('btitle')
        book.bpub_date = datetime.strptime(book_dict.get('bpub_date'), '%Y-%m-%d').date()
        book.save()

        return JsonResponse({
            'id': book.id,
            'btitle': book.btitle,
            'bpub_date': book.bpub_date,
            'bread': book.bread,
            'bcomment': book.bcomment,
            'image': book.image.url if book.image else ''
        })

    def delete(self, request, pk):
        """
        刪除圖書
        路由: DELETE /books/<pk>/
        """
        try:
            book = BookInfo.objects.get(pk=pk)
        except BookInfo.DoesNotExist:
            return HttpResponse(status=404)

        book.delete()

        return HttpResponse(status=204)
# urls.py

urlpatterns = [
    url(r'^books/$', views.BooksAPIVIew.as_view()),
    url(r'^books/(?P<pk>\d+)/$', views.BookAPIView.as_view())
]

測試

使用Postman測試上述接口

1) 獲取所有圖書數據

GET 方式訪問 http://127.0.0.1:8000/books/, 返回狀態碼200,數據如下

[
    {
        "id": 1,
        "btitle": "射雕英雄傳",
        "bpub_date": "1980-05-01",
        "bread": 12,
        "bcomment": 34,
        "image": ""
    },
    {
        "id": 2,
        "btitle": "天龍八部",
        "bpub_date": "1986-07-24",
        "bread": 36,
        "bcomment": 40,
        "image": ""
    },
    {
        "id": 3,
        "btitle": "笑傲江湖",
        "bpub_date": "1995-12-24",
        "bread": 20,
        "bcomment": 80,
        "image": ""
    },
    {
        "id": 4,
        "btitle": "雪山飛狐",
        "bpub_date": "1987-11-11",
        "bread": 58,
        "bcomment": 24,
        "image": ""
    },
    {
        "id": 5,
        "btitle": "西游記",
        "bpub_date": "1988-01-01",
        "bread": 10,
        "bcomment": 10,
        "image": "booktest/xiyouji.png"
    },
    {
        "id": 6,
        "btitle": "水滸傳",
        "bpub_date": "1992-01-01",
        "bread": 10,
        "bcomment": 11,
        "image": ""
    },
    {
        "id": 7,
        "btitle": "紅樓夢",
        "bpub_date": "1990-01-01",
        "bread": 0,
        "bcomment": 0,
        "image": ""
    }
]

2)獲取單一圖書數據

GET 訪問 http://127.0.0.1:8000/books/5/ ,返回狀態碼200, 數據如下

{
    "id": 5,
    "btitle": "西游記",
    "bpub_date": "1988-01-01",
    "bread": 10,
    "bcomment": 10,
    "image": "booktest/xiyouji.png"
}

GET 訪問http://127.0.0.1:8000/books/100/,返回狀態碼404

3)新增圖書數據

POST 訪問http://127.0.0.1:8000/books/,發送JSON數據:

{
    "btitle": "三國演義",
    "bpub_date": "1990-02-03"
}

返回狀態碼201,數據如下

{
    "id": 8,
    "btitle": "三國演義",
    "bpub_date": "1990-02-03",
    "bread": 0,
    "bcomment": 0,
    "image": ""
}

4)修改圖書數據

PUT 訪問http://127.0.0.1:8000/books/8/,發送JSON數據:

{
    "btitle": "三國演義(第二版)",
    "bpub_date": "1990-02-03"
}

返回狀態碼200,數據如下

{
    "id": 8,
    "btitle": "三國演義(第二版)",
    "bpub_date": "1990-02-03",
    "bread": 0,
    "bcomment": 0,
    "image": ""
}

5)刪除圖書數據

DELETE 訪問http://127.0.0.1:8000/books/8/,返回204狀態碼

明確REST接口開發的核心任務

分析一下上節的案例,可以發現,在開發REST API接口時,視圖中做的最主要有三件事:

  • 將請求的數據(如JSON格式)轉換為模型類對象
  • 操作數據庫
  • 將模型類對象轉換為響應的數據(如JSON格式)

序列化Serialization

這里是將字典列表等,轉換為json字符串,這里反序列化是將前端傳遞的數據轉換為數據對象的過程
維基百科中對于序列化的定義:

序列化(serialization)在計算機科學的資料處理中,是指將數據結構或物件狀態轉換成可取用格式(例如存成檔案,存于緩沖,或經由網絡中傳送),以留待后續在相同或另一臺計算機環境中,能恢復原先狀態的過程。依照序列化格式重新獲取字節的結果時,可以利用它來產生與原始物件相同語義的副本。對于許多物件,像是使用大量參照的復雜物件,這種序列化重建的過程并不容易。面向對象中的物件序列化,并不概括之前原始物件所關聯的函式。這種過程也稱為物件編組(marshalling)。從一系列字節提取數據結構的反向操作,是反序列化(也稱為解編組, deserialization, unmarshalling)。
序列化在計算機科學中通常有以下定義:

? 在數據儲存與傳送的部分是指將一個對象。這程序被應用在不同應用程序之間傳送對象),以及服務器將對象)儲存到檔案或數據庫。相反的過程又稱為反序列化。

簡而言之,我們可以將序列化理解為:

將程序中的一個數據結構類型轉換為其他格式(字典、JSON、XML等),例如將Django中的模型類對象裝換為JSON字符串,這個轉換過程我們稱為序列化。

如:

queryset = BookInfo.objects.all()
book_list = []
# 序列化
for book in queryset:
    book_list.append({
        'id': book.id,
        'btitle': book.btitle,
        'bpub_date': book.bpub_date,
        'bread': book.bread,
        'bcomment': book.bcomment,
        'image': book.image.url if book.image else ''
    })
return JsonResponse(book_list, safe=False)

反之,將其他格式(字典、JSON、XML等)轉換為程序中的數據,例如將JSON字符串轉換為Django中的模型類對象,這個過程我們稱為反序列化。

如:

json_bytes = request.body
json_str = json_bytes.decode()

# 反序列化
book_dict = json.loads(json_str)
book = BookInfo.objects.create(
    btitle=book_dict.get('btitle'),
    bpub_date=datetime.strptime(book_dict.get('bpub_date'), '%Y-%m-%d').date()

我們可以看到,在開發REST API時,視圖中要頻繁的進行序列化與反序列化的編寫。

總結

在開發REST API接口時,我們在視圖中需要做的最核心的事是:

  • 將數據庫數據序列化為前端所需要的格式,并返回;

  • 將前端發送的數據反序列化為模型類對象,并保存到數據庫中。

DjangoRESTframework簡介

  1. 在序列化與反序列化時,雖然操作的數據不盡相同,但是執行的過程卻是相似的,也就是說這部分代碼是可以復用簡化編寫的。
  2. 在開發REST API的視圖中,雖然每個視圖具體操作的數據不同,但增、刪、改、查的實現流程基本套路化,所以這部分代碼也是可以復用簡化編寫的:
  • :校驗請求數據 -> 執行反序列化過程 -> 保存數據庫 -> 將保存的對象序列化并返回
  • :判斷要刪除的數據是否存在 -> 執行數據庫刪除
  • :判斷要修改的數據是否存在 -> 校驗請求的數據 -> 執行反序列化過程 -> 保存數據庫 -> 將保存的對象序列化并返回
  • :查詢數據庫 -> 將數據序列化并返回
    Django REST framework可以幫助我們簡化上述兩部分的代碼編寫,大大提高REST API的開發速度。

認識Django REST framework

在這里插入圖片描述

Django REST framework 框架是一個用于構建Web API 的強大而又靈活的工具。

通常簡稱為DRF框架 或 REST framework。

DRF框架是建立在Django框架基礎之上,由Tom Christie大牛二次開發的開源項目。

特點

  • 提供了定義序列化器Serializer的方法,可以快速根據 Django ORM 或者其它庫自動序列化/反序列化;
  • 提供了豐富的類視圖、Mixin擴展類,簡化視圖的編寫;
  • 豐富的定制層級:函數視圖、類視圖、視圖集合到自動生成 API,滿足各種需要;
  • 多種身份認證和權限認證方式的支持;
  • 內置了限流系統;
  • 直觀的 API web 界面;
  • 可擴展性,插件豐富

資料:

  • 官方文檔
  • Github源碼

筆記

  1. 后臺主要是給運營人員用,主要用來增加商品信息,運營好像不會編程,要提供圖形化界面,后臺的前端頁面已經寫完了,需要我們補充后端邏輯

  2. 前后端不分離需要后端模板渲染,前后端分離在進行數據加載的時候統一返回json數據,由前端進行渲染

  3. 美多商城是一個BtoC(企業對用戶)的模式,淘寶是CtoC的模式,還有BtoB模式,

  4. 電商類網站是在所有網站中包含功能最多的,會開發電商,開發其他網站就比較容易了,做新的網站時,可以借鑒美多商城的實現方案

  5. 在這里插入圖片描述

  6. 在這里插入圖片描述

  7. 在這里插入圖片描述
    這樣使用 127.0.0.1:8000 都可以進行訪問

  8. 這里修改前端html頁面時進行了手動服務器刷新

  9. mounted 指向的函數是頁面加載完成之后執行的

  10. 在這里插入圖片描述
    有了接口,前端和后端就能進行代碼的編寫,接口是由后端人員進行定義的

  11. 在這里插入圖片描述
    前后端分離使用前端進行跳轉
    這是跳轉代碼

  12. 前后端分離的結果由測試人員進行查看

  13. 可以使用postman自己進行測試,測試小組發現bug會發郵件給開發小組

  14. 在這里插入圖片描述

  15. 后端的工作主要就是設計接口,寫后端邏輯

  16. python manage.py 輸入后會顯示這個模塊的功能選項信息

  17. 創建完應用要進行注冊,否則可能沒法生成遷移文件或是使用模板

  18. 在這里插入圖片描述
    連接成功點擊apply

  19. 函數參數內可放字典,數據之間允許有空白符,字典 k 冒號 v 之間允許有空白符 v 可以寫一個表達式,字典存放的是表達式計算的結果

  20. 在這里插入圖片描述

  21. postman 測試完成后,就可以給測試小組

  22. 字典通過 [],獲取參數,不存在的話會拋出異常,如果通過get方式,沒有取到數據的話會返回None

  23. 在這里插入圖片描述
    判斷數據是否為空

  24. json.loads 返回的是字典

  25. 在這里插入圖片描述
    get不到東西會拋出異常

  26. 在這里插入圖片描述
    這是物理刪除

原文鏈接:https://blog.csdn.net/weixin_43297727/article/details/114891761

欄目分類
最近更新