1. 程式人生 > >DRF之簡介以及序列化操作

DRF之簡介以及序列化操作

內部使用 工具 自定義 應用開發 dmi 用戶驗證 pla let cmd

1. Web應用模式.

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

  1. 前後端不分離

技術分享圖片

2.前後端分離

技術分享圖片

2. api接口

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

目前市面上大部分公司開發人員使用的接口服務架構主要有:restful、rpc。

rpc: 翻譯成中文:遠程過程調用[遠程服務調用].

http://www.lufei.com/api

post請求

action=get_all_student&params=301&sex=1

接口多了,對應函數名和參數就多了,前端在請求api接口時,就會比較難找.容易出現重復的接口

restful: 翻譯成中文: 資源狀態轉換.把後端所有的數據/文件都看成資源.那麽接口請求數據,本質上來說就是對資源的操作了.

web項目中操作資源,無非就是增刪查改.所以要求在地址欄中聲明要操作的資源是什麽,然後通過http請求動詞來說明對資源進行哪一種操作.

  POST http://www.lufei.com/api/students/ 添加數據

  GET http://www.lufei.com/api/students/ 獲取所有學生

3. RESTful API規範

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

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

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

而對於數據資源分別使用POST、DELETE、GET、UPDATE等請求動作來表達對數據的增刪查改。

| 請求方法 | 請求地址 | 後端操作 |
| -------- | ----------- | ----------------- |
| GET | /students | 獲取所有學生 |
| POST | /students | 增加學生 |

| GET | /students/1 | 獲取編號為1的學生 |
| PUT | /students/1 | 修改編號為1的學生 |
| DELETE | /students/1 | 刪除編號為1的學生 |

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

參考文檔:http://www.runoob.com/w3cnote/restful-architecture.html

4. 序列化

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

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

例如:我們在django中獲取到的數據默認是模型對象,但是模型對象數據無法直接提供給前端或別的平臺使用,所以我們需要把數據進行序列化,變成字符串或者json數據,提供給別人。

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

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

5. Django Rest_Framework

核心思想: 縮減編寫api接口的代碼

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

中文文檔:https://q1mi.github.io/Django-REST-framework-documentation/#django-rest-framework

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

特點

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

  • 提供了豐富的類視圖、Mixin擴展類,簡化視圖的編寫;

  • 豐富的定制層級:函數視圖、類視圖、視圖集合到自動生成 API,滿足各種需要;

  • 多種身份認證和權限認證方式的支持;[jwt]

  • 內置了限流系統;

  • 直觀的 API web 界面;

  • 可擴展性,插件豐富

6. 環境安裝與配置

DRF需要以下依賴:

  • Python (2.7, 3.2, 3.3, 3.4, 3.5, 3.6)

  • Django (1.10, 1.11, 2.0)

DRF是以Django擴展應用的方式提供的,所以我們可以直接利用已有的Django環境而無需從新創建。(若沒有Django環境,需要先創建環境安裝Django)

6.1 安裝DRF

pip install djangorestframework

6.2 添加rest_framework應用

  在settings.pyINSTALLED_APPS中添加‘rest_framework‘。

INSTALLED_APPS = [
    ...
    ‘rest_framework‘,
]

接下來就可以使用DRF進行開發了。在項目中如果使用rest_framework框架實現API接口,主要有以下三個步驟:

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

  • 操作數據庫

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

創建django項目:

  註意,最好使用cmd窗口命令創建原生django項目,不要用pycharm自帶的工具創建,避免pycharm自身增加的一些配置,防止在項目合作開發時遇到合並代碼時出現問題;

django-admin.py startproject mysite

一個完整的使用模型序列化的示例:

url:

技術分享圖片
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path(admin/, admin.site.urls),
    path(api/, include("demo1.urls")),
]
View Code

demo1 url:

技術分享圖片
from rest_framework.routers import DefaultRouter
from .views import BookInfoAPIView
urlpatterns = []

# 創建路由對象
routers = DefaultRouter()
# 通過路由對象對視圖類進行路由生成
routers.register("books",BookInfoAPIView)

urlpatterns+=routers.urls
View Code

demo1 view試圖:

技術分享圖片
from rest_framework.viewsets import ModelViewSet
from demo1.models import BookInfo
from .serializers import BookInfoSerializer
# Create your views here.
class BookInfoAPIView(ModelViewSet):
    # 當前視圖類所有方法使用得數據結果集是誰?
    queryset = BookInfo.objects.all()
    # 當前視圖類使用序列化器類是誰
    serializer_class = BookInfoSerializer
View Code

models:

技術分享圖片
from django.db import models

# Create your models here.

# Create your models here.
#定義圖書模型類BookInfo
class BookInfo(models.Model):
    btitle = models.CharField(max_length=20, verbose_name=圖書標題)
    bpub_date = models.DateField(verbose_name=出版時間)
    bread = models.IntegerField(default=0, verbose_name=閱讀量)
    bcomment = models.IntegerField(default=0, verbose_name=評論量)
    is_delete = models.BooleanField(default=False, verbose_name=邏輯刪除)

    class Meta:
        db_table = tb_books  # 指明數據庫表名
        verbose_name = 圖書  # 在admin站點中顯示的名稱
        verbose_name_plural = verbose_name  # 顯示的復數名稱

    def __str__(self):
        """定義每個數據對象的顯示信息"""
        return "圖書:《"+self.btitle+""


#定義英雄模型類HeroInfo
class HeroInfo(models.Model):
    GENDER_CHOICES = (
        (0, ),
        (1, )
    )
    hname = models.CharField(max_length=20, verbose_name=名稱)
    hgender = models.SmallIntegerField(choices=GENDER_CHOICES, default=0, verbose_name=性別)
    hcomment = models.CharField(max_length=200, null=True, verbose_name=技能描述)
    hbook = models.ForeignKey(BookInfo, on_delete=models.CASCADE, verbose_name=所屬圖書)  # 外鍵
    is_delete = models.BooleanField(default=False, verbose_name=邏輯刪除)

    class Meta:
        db_table = tb_heros
        verbose_name = 英雄
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.hname
View Code

運行項目後,可進行增刪改查操作:

  在瀏覽器中輸入網址127.0.0.1:8000,可以看到DRF提供的API Web瀏覽頁面:

  點擊鏈接127.0.0.1:8000/books/ 可以訪問獲取所有數據的接口(查看所有,添加)

  在瀏覽器中輸入網址127.0.0.1:8000/books/d+/,可以訪問獲取單一圖書信息的接口(id為d+的圖書) (刪除.修改)

序列化與反序列化(不包含DRF的視圖操作):用法和forms組件很相像

序列化demo:

drf-url:

技術分享圖片
from django.contrib import admin
from django.urls import path, include



urlpatterns = [
    path(admin/, admin.site.urls),
    path(api/, include("demo1.urls")),
    path(ser/, include("ser.urls")),
]
View Code

ser-url:

技術分享圖片
from django.urls import path, re_path
from . import views
urlpatterns = [
    path("books/",views.BookInfoView.as_view()),

]
View Code

views:

技術分享圖片
from django.http import JsonResponse
from django.shortcuts import render
from django.views import View
from demo1.models import BookInfo


from .serializers import BookInfoSerializer
class BookInfoView(View):
    def get(self,request):
        # 操作數據庫
        books = BookInfo.objects.all()
        # 創建序列化器對象
        # 參數1: instance=要序列化的模型數據
        # 參數2: data=要反序列化器的字典數據
        # 參數3: many= 是否要序列化多個模型數據,多條數據many=True,默認一條數據
        # 參數4: context=序列化器使用的上下文,字典類型數據,可以通過context把視圖中的數據,傳遞給序列化器內部使用
        serializer = BookInfoSerializer(instance=books, many=True)

        # 通過 serializer.data 獲取序列化完成以後的數據
        print(serializer.data)
        # 返回數據
        return JsonResponse(serializer.data, safe=False)
View Code

serialize:

技術分享圖片
###############################################################################################################################
# 1. 序列化器的序列化階段使用
###############################################################################################################################
from rest_framework import serializers
class BookInfoSerializer(serializers.Serializer):
#     # 自定義要序列化的字段
    id = serializers.IntegerField(label="主鍵ID",read_only=True)
    btitle=serializers.CharField(label="圖書標題")
    bpub_date = serializers.DateField(label="出版日期")
    bread=serializers.IntegerField(label="閱讀量")
    bcomment=serializers.IntegerField(label="評論量")
    is_delete=serializers.BooleanField(label="邏輯刪除")
View Code

反序列化demo:

序列化的表格可以和反序列化的表公用,而且反序列化的表更加強大,因為還能驗證數據,所以一般只保留反序列化的序列化表,不過對於一些只讀的字段,可添加read_only 字段選項來避免

drf-url:

技術分享圖片
from django.contrib import admin
from django.urls import path, include



urlpatterns = [
    path(admin/, admin.site.urls),
    # path(‘api/‘, include("demo1.urls")),
    path(ser/, include("ser.urls")),
]
View Code

ser-urls:

技術分享圖片
from django.urls import path, re_path
from . import views
urlpatterns = [
    path("books2/", views.BookInfo2View.as_view()),
    re_path("books2/(?P<pk>\d+)/", views.BookInfo2View.as_view()),

]
View Code

views:

技術分享圖片
from django.views import View
from django.http import JsonResponse
from django.http import QueryDict
from .serializers import BookInfo2Serializer
from demo1.models import BookInfo

###############################################################################################################################
# 2 序列化器的反序列化階段使用
# 主要用戶驗證數據和字典數據轉換成模型
# 為了保證測試順利進行,我們在settings.py中關閉 csrf的中間件
# ‘django.middleware.csrf.CsrfViewMiddleware‘,
###############################################################################################################################

class BookInfo2View(View):
    def get(self,request):
        # 操作數據庫
        books = BookInfo.objects.all()
        # 創建序列化器對象
        # 參數1: instance=要序列化的模型數據
        # 參數2: data=要反序列化器的字典數據
        # 參數3: many= 是否要序列化多個模型數據,多條數據many=True,默認一條數據
        # 參數4: context=序列化器使用的上下文,字典類型數據,可以通過context把視圖中的數據,傳遞給序列化器內部使用
        serializer = BookInfo2Serializer(instance=books, many=True)

        # 通過 serializer.data 獲取序列化完成以後的數據
        print(serializer.data)
        # 返回數據
        return JsonResponse(serializer.data, safe=False)


    def post(self, request):
        """添加一本圖書"""
        # 接受數據
        data = request.POST
        # 反序列化
        serializer = BookInfo2Serializer(data=data)
        # 1. 驗證數據
        # raise_exception=True 把驗證的錯誤信息返回給客戶端,同時阻止程序繼續往下執行
        serializer.is_valid(raise_exception=True)
        # is_valid調用驗證方式: 字段選項validators->自定義驗證方法[單字段]->自定義驗證方法[多字段]
        # 驗證成功後的數據
        # print(serializer.validated_data)

        # 2. 轉換數據成模型,同步到數據庫中
        result = serializer.save()  # save會自動調用序列化器類裏面聲明的create/update方法,返回值是當前新增/更新的模型對象

        # 響應數據
        return JsonResponse(serializer.data)

    def put(self,request,pk):
        """更新一個圖書"""
        # 根據id主鍵獲取指定圖書信息
        instance=BookInfo.objects.get(pk=pk)
        data = QueryDict(request.body)

        # 使用序列化器完成驗證和反序列化過程
        # partial=True 接下裏在反序列化中允許部分數據更新
        serializer = BookInfo2Serializer(instance=instance,data=data,partial=True)
        serializer.is_valid(raise_exception=True)

        # save之所以可以自動識別,什麽時候執行create ,什麽時候執行update
        # 主要是看創建序列化器對象時,是否有傳入instance參數,
        # 有instance參數,則save會調用序列化器內部的update方法
        # 沒有instance參數,則save會調用序列化器內部的create方法
        result = serializer.save()

        return JsonResponse(serializer.data)
View Code

serialize:

技術分享圖片
###############################################################################################################################
# 2 序列化器的反序列化階段使用
# 主要用戶驗證數據和字典數據轉換成模型
###############################################################################################################################
from rest_framework import serializers
from demo1.models import BookInfo

# 自定義驗證字段選項函數,很少用
def check_btitle(data):
    if data=="西廂記":
        raise serializers.ValidationError("西廂記也是禁書~")
    # 一定要返回數據
    return data

class BookInfo2Serializer(serializers.Serializer):
    # 自定義要反序列化的字段
    id = serializers.IntegerField(label="主鍵ID",read_only=True)
    btitle = serializers.CharField(label="標題",required=True,min_length=1,max_length=20,validators=[check_btitle])  #通過validators=[check_btitle,]調用自定義選項函數
    bpub_date=serializers.DateField(label="出版日期")
    bread=serializers.IntegerField(label="閱讀量",min_value=0)
    bcomment=serializers.IntegerField(label="評論量",default=0)
    # required=False 反序列化時, 當前字段可以不填
    is_delete=serializers.BooleanField(label="邏輯刪除")

    # 自定義驗證方法[驗證單個字段,可以有多個方法]
    #寫法必須是 def validate_<字段名>(self,data): # data當前字段對應的值
    def validate_btitle(self,data):
        # 例如,圖書名不能是紅樓夢
        if data=="紅樓夢":
            # 拋出錯誤
            raise serializers.ValidationError("紅樓夢是禁書~")
        # 驗證方法中,把數據值必須返回給字段,否則字段值為空
        return data

    # 自定義驗證方法[驗證多個或者所有字段,只能出現一次] 寫法必須是validate(self,data)
    def validate(self,data): # data 這個是所有字段的內容,字典類型
        bread = data.get("bread")
        bcomment = data.get("bcomment")

        if bread>=bcomment:
            return data
        raise serializers.ValidationError("閱讀量小於評論量,數據太假了")


    def create(self,validated_data):
        """
        保存數據,把字典轉換成模型
        validated_data 客戶端提交過來,並經過驗證的數據
        """
        instance = BookInfo.objects.create(
            btitle = validated_data.get("btitle"),
            bread = validated_data.get("bread"),
            bcomment = validated_data.get("bcomment"),
            bpub_date = validated_data.get("bpub_date"),
            is_delete = validated_data.get("is_delete"),
        )
        #返回模型對象
        return instance


    def update(self,instance,validated_data):
        """更新數據
        instance 本次更新操作的模型對象
        validated_data 客戶端提交過來,並經過驗證的數據
        """
        instance.btitle=validated_data.get(btitle)
        instance.bread=validated_data.get(bread)
        instance.bcomment=validated_data.get(bcomment)
        instance.bpub_date=validated_data.get(bpub_date)
        instance.is_delete=validated_data.get(is_delete)

        # 調用ORM的保存更新操作
        instance.save()
        # 返回模型對象
        return instance
######################################################################################
View Code

模型類序列化:相當於modelform組件....用法等

  如果我們想要使用序列化器對應的是Django的模型類,DRF為我們提供了ModelSerializer模型類序列化器來幫助我們快速創建一個Serializer類。

ModelSerializer與常規的Serializer相同,但提供了:

  • 基於模型類自動生成一系列字段

  • 基於模型類自動為Serializer生成validators,比如unique_together

  • 包含默認的create()和update()的實現

demo:

views:

技術分享圖片
###############################################################################################################################
# 3 模型序列化器
# 1. 可以幫我們自動完成字段的聲明[主要是從模型中的字段聲明裏面提取過來]
# 2. 模型序列化器也可以幫我們聲明了create和update方法的代碼
###############################################################################################################################
from django.views import View
from django.http import JsonResponse
from .serializers import BookInfoModelSerializer
class BookInfo3View(View):
    def post(self, request):
        data = request.POST
        serializer = BookInfoModelSerializer(data=data)
        serializer.is_valid(raise_exception=True)
        result = serializer.save()
        # 響應數據
        return JsonResponse(serializer.data)

    def put(self, request, pk):
        """更新一個圖書"""
        book = BookInfo.objects.get(pk=pk)

        # 獲取put提交的數據
        data = QueryDict(request.body)

        #允許部分更新partial=True
        serializer = BookInfoModelSerializer(instance=book, data=data, partial=True)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        # 響應數據
        return JsonResponse(serializer.data)
View Code

serialize:

技術分享圖片
######################################################################################

from rest_framework import serializers
from demo1.models import BookInfo

class BookInfoModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = BookInfo
        # fields = ["id", "btitle"]
        fields=__all__
        # 可以給模型序列化器裏面指定的字段設置限制選項
        extra_kwargs = {
            "bread": {"min_value": 0, "required": True},
        }
    def validate_btitle(self,data):
        # 例如,圖書名不能是紅樓夢
        if data=="紅樓夢":
            # 拋出錯誤
            raise serializers.ValidationError("紅樓夢是禁書~")
        # 驗證方法中,把數據值必須返回給字段,否則字段值為空
        return data

    # 自定義驗證方法[驗證多個或者所有字段,只能出現一次]
    def validate(self,data): # data 這個是所有字段的內容,字典類型
        bread = data.get("bread")
        bcomment = data.get("bcomment")

        if bread>=bcomment:
            return data
        raise serializers.ValidationError("閱讀量小於評論量,數據太假了")
View Code

DRF之簡介以及序列化操作