開發框架和序列化

Web應用模式

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

1. 先後端不分離前端

 

 

2. 先後端分離python

 

 

api接口

爲了在團隊內部造成共識、防止我的習慣差別引發的混亂,咱們須要找到一種你們都以爲很好的接口實現規範,並且這種規範可以讓後端寫的接口,用途一目瞭然,減小雙方之間的合做成本。mysql

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

 

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

http://www.lufei.com/api

post請求

action=get_all_student&params=301&sex=1

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

 

restful: 翻譯成中文: 資源狀態轉換.

把後端全部的數據/文件都當作資源.

那麼接口請求數據,本質上來講就是對資源的操做了.

web項目中操做資源,無非就是增刪查改.因此要求在地址欄中聲明要操做的資源是什麼,而後經過http請求動詞來講明對資源進行哪種操做.

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

GET http://www.lufei.com/api/students/ 獲取全部學生

DELETE http://www.lufei.com/api/students/<pk> 刪除1個學生

GET http://www.lufei.com/api/students/<pk> 獲取一個學生

 

 

RESTful API規範

REST全稱是Representational State Transfer,中文意思是表述(編者注:一般譯爲表徵)性狀態轉移。 它首次出如今2000年Roy Fielding的博士論文中。github

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

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

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

 

請求方法 請求地址 後端操做
GET /students 獲取全部學生
POST /students 增長學生
GET /students/<pk> 獲取編號爲pk的學生
PUT /students/<pk> 修改編號爲pk的學生
DELETE /students/<pk> 刪除編號爲pk的學生

  

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

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

 

 

序列化

 

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://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 界面;
- 可擴展性,插件豐富

 

 

環境安裝與配置(重點)

 前提是已經安裝了django,建議安裝在虛擬環境

# mkvirtualenv drfdemo -p python3
# pip install django

pip install djangorestframework
pip install pymysql

 

linux 複製 shift+insert

1.1 建立django項目

 

cd ~/Desktop
django-admin startproject drfdemo

 

 

 使用pycharm打開項目,設置虛擬環境的解析器,並修改manage.py中的後綴參數。

2 添加rest_framework應用

settings.pyINSTALLED_APPS中添加'rest_framework'。

INSTALLED_APPS = [
    ...
    'rest_framework',
]

接下來就可使用DRF提供的功能進行api接口開發了。在項目中若是使用rest_framework框架實現API接口,主要有如下三個步驟:

 

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

  • 操做數據庫

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

 接下來,咱們快速體驗下四天後咱們學習完成drf之後的開發代碼。接下來代碼不須要理解,看步驟。

 

3 體驗drf徹底簡寫代碼的過程

3.1. 建立模型操做類

 

class Student(models.Model):
    # 模型字段
    name = models.CharField(max_length=100,verbose_name="姓名")
    sex = models.BooleanField(default=1,verbose_name="性別")
    age = models.IntegerField(verbose_name="年齡")
    class_null = models.CharField(max_length=5,verbose_name="班級編號")
    description = models.TextField(max_length=1000,verbose_name="個性簽名")

    class Meta:
        db_table="tb_student"
        verbose_name = "學生"
        verbose_name_plural = verbose_name

 

爲了方便測試,因此咱們能夠先建立一個數據庫。

create database students charset=utf8;

 

3.1.1 執行數據遷移

例如,在django項目中建立學生子應用。

python manage.py startapp students

把students子應用添加到INSTALLED_APPS中

 

 

 初始化數據庫鏈接

安裝pymysql
pip install pymysql

主引用中__init__.py設置使用pymysql做爲數據庫驅動

import pymysql
pymysql.install_as_MySQLdb()

settings.py配置文件中設置mysql的帳號密碼

DATABASES = {
    # 'default': {
    #     'ENGINE': 'django.db.backends.sqlite3',
    #     'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    # },
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': "students",
        "HOST": "127.0.0.1",
        "PORT": 3306,
        "USER": "root",
        "PASSWORD":"123",
    }
}

 

終端下,執行數據遷移。

python manage.py makemigrations
python manage.py migrate

錯誤列表

執行數據遷移 python manage.py makemigrations 報錯以下:

 

 

 

解決方案:

註釋掉 backends/mysql/base.py中的35和36行代碼。

 

 

 

# 執行數據遷移發生如下錯誤:

 

 

 

解決方法:

backends/mysql/operations.py146行裏面新增一個行代碼:

 

 

 

 

 

3.2. 建立序列化器

 

在students應用目錄中新建serializers.py用於保存該應用的序列化器。

建立一個StudentModelSerializer用於序列化與反序列化。

# 建立序列化器類,回頭會在試圖中被調用
from rest_framework import serializers
from .models import Student

class StudentModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = Student
        fields = "__all__"

 

  • model 指明該序列化器處理的數據字段從模型類BookInfo參考生成

  • fields 指明該序列化器包含模型類中的哪些字段,'all'指明包含全部字段

3.3. 編寫視圖

在students應用的views.py中建立視圖StudentViewSet,這是一個視圖集合。

from rest_framework.viewsets import ModelViewSet
from .models import Student
from .serializers import StudentModelSerializer
# Create your views here.
class StudentViewSet(ModelViewSet):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer
  • queryset 指明該視圖集在查詢數據時使用的查詢集

  • serializer_class 指明該視圖在進行序列化或反序列化時使用的序列化器

3.4. 定義路由

在students應用的urls.py中定義路由信息。

from . import views
from rest_framework.routers import DefaultRouter

# 路由列表
urlpatterns = []

router = DefaultRouter()  # 能夠處理視圖的路由器
router.register('students', views.StudentViewSet)  # 向路由器中註冊視圖集

urlpatterns += router.urls  # 將路由器中的因此路由信息追到到django的路由列表中

 

最後把students子應用中的路由文件加載到總路由文件中.

from django.contrib import admin
from django.urls import path,include

urlpatterns = [
    path('admin/', admin.site.urls),
    path("stu/",include("students.urls")),
]

 

3.5. 運行測試

運行當前程序(與運行Django同樣)

python manage.py runserver

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

 

 

 

1)點擊連接127.0.0.1:8000/stu/students 能夠訪問獲取全部數據的接口,呈現以下頁面:

 

 

2)在頁面底下表單部分填寫學生信息,能夠訪問添加新學生的接口,保存學生信息:

 

 點擊POST後,返回以下頁面信息:

 

 

3)在瀏覽器中輸入網址127.0.0.1:8000/stu/students/5/,能夠訪問獲取單一學生信息的接口(id爲5的學生),呈現以下頁面:

 

 

4)在頁面底部表單中填寫學生信息,能夠訪問修改學生的接口

 

 

點擊PUT,返回以下頁面信息:

 

 

5)點擊DELETE按鈕,能夠訪問刪除學生的接口

 

 

返回,以下頁面:

 

序列化器-Serializer

 做用:

 

1. 序列化,序列化器會把模型對象轉換成字典,通過response之後變成json字符串
2. 反序列化,把客戶端發送過來的數據,通過request之後變成字典,序列化器能夠把字典轉成模型
3. 反序列化,完成數據校驗功能

 

定義序列化器

 

Django REST framework中的Serializer使用類來定義,須繼承自rest_framework.serializers.Serializer。

接下來,爲了方便演示序列化器的使用,咱們先建立一個新的子應用ser

 

python manage.py startapp ser

 

 咱們已有了一個數據庫模型類students/Student

 models

from django.db import models

# Create your models here.
class Student(models.Model):
    # 模型字段
    name = models.CharField(max_length=100,verbose_name="姓名",help_text="提示文本:帳號不能爲空!")
    sex = models.BooleanField(default=True,verbose_name="性別")
    age = models.IntegerField(verbose_name="年齡")
    class_null = models.CharField(max_length=5,verbose_name="班級編號")
    description = models.TextField(verbose_name="個性簽名")

    class Meta:
        db_table="tb_student"
        verbose_name = "學生"
        verbose_name_plural = verbose_name
View Code

建立序列化器

定義以下:

在ser應用下建立一個serializers.py

from rest_framework import serializers

# 聲明序列化器,全部的序列化器都要直接或者間接繼承於 Serializer
# 其中,ModelSerializer是Serializer的子類,ModelSerializer在Serializer的基礎上進行了代碼簡化
class StudentSerializer(serializers.Serializer):
    """學生信息序列化器"""
    # 1. 須要進行數據轉換的字段
    id = serializers.IntegerField()
    name = serializers.CharField()
    age = serializers.IntegerField()
    sex = serializers.BooleanField()
    description = serializers.CharField()

    # 2. 若是序列化器集成的是ModelSerializer,則須要聲明調用的模型信息

    # 3. 驗證代碼

    # 4. 編寫添加和更新模型的代碼

注意:serializer不是隻能爲數據庫模型類定義,也能夠爲非數據庫模型類的數據定義。serializer是獨立於數據庫以外的存在。

經常使用字段類型

 

字段 字段構造方式 serializers.字段構造方式()
BooleanField BooleanField()
NullBooleanField NullBooleanField()
CharField CharField(max_length=None, min_length=None, allow_blank=False, trim_whitespace=True)
EmailField EmailField(max_length=None, min_length=None, allow_blank=False)
RegexField RegexField(regex, max_length=None, min_length=None, allow_blank=False)
SlugField SlugField(maxlength=50, min_length=None, allow_blank=False) 正則字段,驗證正則模式 [a-zA-Z0-9-]+
URLField URLField(max_length=200, min_length=None, allow_blank=False)
UUIDField UUIDField(format='hex_verbose') format: 1) 'hex_verbose'"5ce0e9a5-5ffa-654b-cee0-1238041fb31a" 2) 'hex'"5ce0e9a55ffa654bcee01238041fb31a" 3) 'int' - 如: "123456789012312313134124512351145145114" 4) 'urn' 如: "urn:uuid:5ce0e9a5-5ffa-654b-cee0-1238041fb31a"
IPAddressField IPAddressField(protocol='both', unpack_ipv4=False, **options)
IntegerField IntegerField(max_value=None, min_value=None)
FloatField FloatField(max_value=None, min_value=None)
DecimalField DecimalField(max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None) max_digits: 最多位數 decimal_palces: 小數點位置
DateTimeField DateTimeField(format=api_settings.DATETIME_FORMAT, input_formats=None)
DateField DateField(format=api_settings.DATE_FORMAT, input_formats=None)
TimeField TimeField(format=api_settings.TIME_FORMAT, input_formats=None)
DurationField DurationField()
ChoiceField ChoiceField(choices) choices與Django的用法相同
MultipleChoiceField MultipleChoiceField(choices)
FileField FileField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)
ImageField ImageField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)
ListField ListField(child=, min_length=None, max_length=None)
DictField DictField(child=)

 

選項參數:

參數名稱 做用
max_length 最大長度
min_lenght 最小長度
allow_blank 是否容許爲空
trim_whitespace 是否截斷空白字符
max_value 最大數值
min_value 最小數值

 

通用參數:

參數名稱 說明
read_only 代表該字段僅用於序列化輸出,默認False
write_only 代表該字段僅用於反序列化輸入,默認False
required 代表該字段在反序列化時必須輸入,默認True
default 反序列化時使用的默認值
allow_null 代表該字段是否容許傳入None,默認False
validators 該字段使用的驗證器
error_messages 包含錯誤編號與錯誤信息的字典
label 用於HTML展現API頁面時,顯示的字段名稱
help_text 用於HTML展現API頁面時,顯示的字段幫助提示信息

 

建立Serializer對象

from rest_framework import serializers
class BookSerializer(serializers.Serializer): #序列化器 Serializer

 

定義好Serializer類後,就能夠建立Serializer對象了。

Serializer的構造方法爲:

Serializer(instance=None, data=empty, **kwarg)

說明:

1)用於序列化時,將模型類對象傳入instance參數

2)用於反序列化時,將要被反序列化的數據傳入data參數

3)除了instance和data參數外,在構造Serializer對象時,還可經過context參數額外添加數據,如

serializer = StudentSerializer(instance, context={'request': request})

 

經過context參數附加的數據,能夠經過Serializer對象的context屬性獲取。

  1. 使用序列化器的時候必定要注意,序列化器聲明瞭之後,不會自動執行,須要咱們在視圖中進行調用才能夠。

  2. 序列化器沒法直接接收數據,須要咱們在視圖中建立序列化器對象時把使用的數據傳遞過來。

  3. 序列化器的字段聲明相似於咱們前面使用過的表單系統。

  4. 開發restful api時,序列化器會幫咱們把模型數據轉換成字典.

  5. drf提供的視圖會幫咱們把字典轉換成json,或者把客戶端發送過來的數據轉換字典.

序列化器的使用

序列化

1.基本使用

1) 先查詢出一個學生對象

from students.models import Student

student = Student.objects.get(id=3)

2) 構造序列化器對象

from .serializers import StudentSerializer

serializer = StudentSerializer(instance=student)

3)獲取序列化數據

經過data屬性能夠獲取序列化後的數據

serializer.data
# {'id': 4, 'name': '小張', 'age': 18, 'sex': True, 'description': '猴賽雷'}

 

完整視圖代碼:

from django.views import View
from students.models import Student
from .serializers import StudentSerializer
from django.http.response import JsonResponse
class StudentRetrieveView(View):
    """使用序列化器序列化轉換單個模型數據"""
    def get(self,request,pk):
        # 獲取數據
        student = Student.objects.get(pk=pk)
        # 數據轉換[序列化過程]
        serializer = StudentSerializer(instance=student)
        print(serializer.data)
        # 響應數據
        return JsonResponse(serializer.data)

 

4)若是要被序列化的是包含多條數據的查詢集QuerySet,能夠經過添加many=True參數補充說明

class StudentView(View):    
    """使用序列化器序列化轉換多個模型數據"""
    def get(self,request):
        # 獲取數據
        student_list = Student.objects.all()

        # 轉換數據[序列化過程]
        # 若是轉換多個模型對象數據,則須要加上many=True
        serializer = StudentSerializer(instance=student_list,many=True)
        print( serializer.data ) # 序列化器轉換後的數據

        # 響應數據給客戶端
        # 返回的json數據,若是是列表,則須要聲明safe=False
        return JsonResponse(serializer.data,safe=False)
    
    
    # 訪問結果:
    # [OrderedDict([('id', 1), ('name', 'xiaoming'), ('age', 20), ('sex', True), ('description', '測試')]), OrderedDict([('id', 2), ('name', 'xiaohui'), ('age', 22), ('sex', True), ('description', '後面來的測試')]), OrderedDict([('id', 4), ('name', '小張'), ('age', 18), ('sex', True), ('description', '猴賽雷')])]

序列化總結:

 models

from django.db import models

# Create your models here.
class Student(models.Model):
    # 模型字段
    name = models.CharField(max_length=100,verbose_name="姓名")
    sex = models.BooleanField(default=1,verbose_name="性別")
    age = models.IntegerField(verbose_name="年齡")
    class_null = models.CharField(max_length=5,verbose_name="班級編號")
    description = models.TextField(max_length=1000,verbose_name="個性簽名")

    class Meta:
        db_table="tb_student"
        verbose_name = "學生"
        verbose_name_plural = verbose_name
View Code

項目下的url

from django.contrib import admin
from django.urls import path,include

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

項目下settings的

INSTALLED_APPS = [
    'rest_framework',
    'ser',
]
View Code

ser下url

from django.urls import re_path,path # re_path正則
from . import views
urlpatterns = [
    path("students/", views.StudentView.as_view() ),
    re_path("students/(?P<pk>\d+)/", views.StudentRetrieveView.as_view() ),
]
View Code

ser下serializers.py,手動建立

from rest_framework import serializers

# 聲明序列化器,全部的序列化器都要直接或者間接繼承於 Serializer
# 其中,ModelSerializer是Serializer的子類,ModelSerializer在Serializer的基礎上進行了代碼簡化
class StudentSerializer(serializers.Serializer):
    """學生信息序列化器"""
    # 1. 須要進行數據轉換的字段
    id = serializers.IntegerField()
    name = serializers.CharField()
    age = serializers.IntegerField()
    sex = serializers.BooleanField()
    description = serializers.CharField()

    # 2. 若是序列化器集成的是ModelSerializer,則須要聲明調用的模型信息

    # 3. 驗證代碼

    # 4. 編寫添加和更新模型的代碼
View Code

ser下views

from django.shortcuts import render

# Create your views here.

from django.views import View
from students.models import Student
from .serializers import StudentSerializer
from django.http import JsonResponse

class StudentView(View):
    """調用序列化器進行序列化操做"""
    def get(self,request):
        """提供全部學生信息"""
        student_list = Student.objects.all()
        print(student_list) # 列表,列表裏面的每個成員都是一個模型對象
        # 使用序列化器進行數據轉換列表的每個成員爲字典
        # 1. 實例化序列化器類
        # StudentSerializer(instance, data, context={視圖中要發送給序列化器中使用的字典數據})
        # 參數instance ,模型對象,這個參數通常用於把模型轉成字典,進行序列化
        # 參數data,客戶端提交的字典數據,這個參數通常用於把字典轉成模型對象,進行校驗數據和反序列化
        # 參數context,有時候,路由或者視圖中有些數據須要傳遞序列化器內部的方法中調用,則能夠context以字典的格式傳遞進行
        # 額外參數: many=True, 表示instance是一個模型列表,此時序列化器在轉換數據的時候能夠進行循環
        serializer = StudentSerializer(student_list, many=True)
        print(111,serializer.data ) # 獲取轉換後的數據

        # 關閉json數據的安全檢測, safe=False
        return JsonResponse(serializer.data,safe=False)

class StudentRetrieveView(View):
    def get(self,request,pk):
        """獲取一個學生信息"""
        student = Student.objects.get(pk=pk)
        print(student)
        serializer = StudentSerializer(student)
        print(serializer.data)

        return JsonResponse(serializer.data)
View Code

 

反序列化

1. 數據驗證

使用序列化器進行反序列化時,須要對數據進行驗證後,才能獲取驗證成功的數據或保存成模型類對象。

在獲取反序列化的數據前,必須調用is_valid()方法進行驗證,驗證成功返回True,不然返回False。

驗證失敗,能夠經過序列化器對象的errors屬性獲取錯誤信息,返回字典,包含了字段和字段的錯誤。若是是非字段錯誤,能夠經過修改REST framework配置中的NON_FIELD_ERRORS_KEY來控制錯誤字典中的鍵名。

驗證成功,能夠經過序列化器對象的validated_data屬性獲取數據。

在定義序列化器時,指明每一個字段的序列化類型和選項參數,自己就是一種驗證行爲。

爲了方便學習,咱們新建一個子應用books。

python manage.py startapp books

在settings.py中的INSTALLED_APPS中新增books子應用

INSTALLED_APPS = [
    # ...
    'ser',
    'unser',
]

 

定義一個圖書的模型和序列化器,

Book模型,代碼:

 models

from django.db import models
class Book(models.Model):
    """圖書模型"""
    title = models.CharField(verbose_name='名稱', max_length=20)
    pub_date = models.DateField(verbose_name='發佈日期')
    read = models.IntegerField(verbose_name='閱讀量',default=0)
    comment = models.IntegerField(verbose_name='評論量', null=True, blank=True)

    class Meta:
        db_table = "tb_book"
        verbose_name="圖書"
        verbose_name_plural=verbose_name

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

執行數據遷移,代碼:

python manage.py makemigrations
python manage.py migrate

 

BookSerializer序列化器,代碼:

 三種驗證方式

1. 外部函數設置爲驗證函數

  函數

  字段內加 validators=[check_title]

 

2. 字段 = serializers.字段類型(驗證選項)

  error_messages={"min_value":"xxxx"}

3. 自定義方法設置校驗方法

  單字段

  def validate_字段(self, data)

    raise serializers.ValidationError()

  多字段

·  def validate(self, data):

    字段 = data.get('字段')

    字段 = data.get('字段')

    raise serializers.ValidationError()

from rest_framework import serializers

# 外部函數設置爲驗證函數
def check_title(data):
    if data == "django入門":
        raise serializers.ValidationError("這本書,我定了,你不能叫")

class BookSerializer(serializers.Serializer):
    # 字段聲明
    # 字段 = serializers.字段類型(驗證選項)
    title = serializers.CharField(required=True, max_length=20,validators=[check_title])
    pub_date = serializers.DateField(required=True)
    read = serializers.IntegerField(default=0,min_value=0, error_messages={"min_value":"對不起,當前字段必須大於或等於0"})
    comment = serializers.IntegerField(allow_null=True)

    # 自定義方法設置校驗方法
    # validate_字段(字段值)    用於驗證指定字段
    # validate(客戶端提交的全部數據)  用於驗證多個字段[聯合驗證]
    def validate_title(self, data):
        if("oldboy" in data):
            # 跑出錯誤
            raise serializers.ValidationError("對不起,圖書名不能包含oldboy!")

        # 必須在校驗完成之後,返回結果
        return data

    def validate(self, data):
        read = data.get("read")
        comment = data.get("comment")

        if(comment>read):
            raise serializers.ValidationError("對不起,閱讀量不能小於評論量,噴子!")

        return data

 

 經過構造序列化器對象,並將要反序列化的數據傳遞給data構造參數,進而進行驗證

 

from book.serializers import BookSerializer
data = {'pub_date': 123}
serializer = BookSerializer(data=data)
serializer.is_valid()  # 返回False
serializer.errors
# {'title': [ErrorDetail(string='This field is required.', code='required')], 'pub_date': [ErrorDetail(string='Date has wrong format. Use one of these formats instead: YYYY[-MM[-DD]].', code='invalid')]}
serializer.validated_data  # {}

data = {'title': 'python'}
serializer = BookSerializer(data=data)
serializer.is_valid()  # True   驗證結果返回值
serializer.errors  # {}  錯誤信息
serializer.validated_data  #  OrderedDict([('btitle', 'python')])

is_valid()方法還能夠在驗證失敗時拋出異常serializers.ValidationError,能夠經過傳遞raise_exception=True參數開啓,REST framework接收到此異常,會向前端返回HTTP 400 Bad Request響應。

 

# Return a 400 response if the data was invalid.
serializer.is_valid(raise_exception=True)

若是以爲這些還不夠,須要再補充定義驗證行爲,可使用如下三種方法:

1) validate_字段名

<field_name>字段進行驗證,如

class BookSerializer(serializers.Serializer):
    """圖書數據序列化器"""
    ...

    def validate_title(self, value):
        if 'django' not in value.lower():
            raise serializers.ValidationError("圖書不是關於Django的")
        return value

 測試

from book.serializers import BookSerializer
data = {'title': 'python'}
serializer = BookSerializer(data=data)
serializer.is_valid()  # False   
serializer.errors
#  {'title': [ErrorDetail(string='圖書不是關於Django的', code='invalid')]}

 

2) validate

在序列化器中須要同時對多個字段進行比較驗證時,能夠定義validate方法來驗證,如

class BookSerializer(serializers.Serializer):
    """圖書序列化器"""
    ...

    def validate(self, attrs):
        read = attrs['read']
        comment = attrs['comment']
        if read < comment:
            raise serializers.ValidationError('閱讀量小於評論量,不能夠經過')
        return attrs

測試

from book.serializers import BookSerializer
data = {'title': 'about django', 'read': 10, 'comment': 20}
s = BookSerializer(data=data)
s.is_valid()  # False
s.errors
#  {'non_field_errors': [ErrorDetail(string='閱讀量小於評論量', code='invalid')]}
3) validators

在字段中添加validators選項參數,也能夠補充驗證行爲,如

def about_django(value):
    if 'django' not in value.lower():
        raise serializers.ValidationError("圖書不是關於Django的")

class BookSerializer(serializers.Serializer):
    """圖書序列化器"""
    id = serializers.IntegerField(label='ID', read_only=True)
    title = serializers.CharField(label='名稱', max_length=20, validators=[about_django])
    pub_date = serializers.DateField(label='發佈日期', required=False)
    read = serializers.IntegerField(label='閱讀量', required=False)
    comment = serializers.IntegerField(label='評論量', required=False)

測試:

from book.serializers import BookSerializer
data = {'title': 'python'}
serializer = BookSerializer(data=data)
serializer.is_valid()  # False   
serializer.errors
#  {'title': [ErrorDetail(string='圖書不是關於Django的', code='invalid')]}

反序列化總結:

setting下的url

from django.contrib import admin
from django.urls import path,include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('unser/', include('unser.urls')),
]
View Code

unser下url

from django.urls import re_path,path
from . import views
urlpatterns = [
    path("books/", views.BookView.as_view()),
]
View Code

models

from django.db import models
class Book(models.Model):
    """圖書模型"""
    title = models.CharField(verbose_name='名稱', max_length=20)
    pub_date = models.DateField(verbose_name='發佈日期')
    read = models.IntegerField(verbose_name='閱讀量',default=0)
    comment = models.IntegerField(verbose_name='評論量', null=True, blank=True)

    class Meta:
        db_table = "tb_book"
        verbose_name="圖書"
        verbose_name_plural=verbose_name

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

serializers

from rest_framework import serializers

# 外部函數設置爲驗證函數
def check_title(data):
    if data == "django入門":
        raise serializers.ValidationError("這本書,我定了,你不能叫")

class BookSerializer(serializers.Serializer):
    # 字段聲明
    # 字段 = serializers.字段類型(驗證選項)
    title = serializers.CharField(required=True, max_length=20,validators=[check_title])
    pub_date = serializers.DateField(required=True)
    read = serializers.IntegerField(default=0,min_value=0, error_messages={"min_value":"對不起,當前字段必須大於或等於0"})
    comment = serializers.IntegerField(allow_null=True)

    # 自定義方法設置校驗方法
    # validate_字段(字段值)    用於驗證指定字段
    # validate(客戶端提交的全部數據)  用於驗證多個字段[聯合驗證]
    def validate_title(self, data):
        if("oldboy" in data):
            # 跑出錯誤
            raise serializers.ValidationError("對不起,圖書名不能包含oldboy!")

        # 必須在校驗完成之後,返回結果
        return data

    def validate(self, data):
        read = data.get("read")
        comment = data.get("comment")

        if(comment>read):
            raise serializers.ValidationError("對不起,閱讀量不能小於評論量,噴子!")

        return data
View Code

views

from django.views import View
from django.http import JsonResponse
from .serializers import BookSerializer


class BookView(View):
    """使用序列化器完成反序列化和驗證數據功能"""

    def get(self, request):
        """添加一個圖書信息"""
        # data_bytes = request.body
        # import json
        # data_dict = json.loads(data_bytes)
        # print(data_dict) # {'title': 'python入門'}

        data_dict = {
            "title": "django入門",
            "pub_date": "2009-10-10",
            # "read": -10000, # 模擬失敗的狀況
            "read": 10000,
            "comment": 300
        }

        # 使用序列化器進行反序列化校驗
        serializer = BookSerializer(data=data_dict)
        ret = serializer.is_valid(raise_exception=True)  # raise_exception=True 主動中止程序執行,並拋出錯誤
        print(ret)
        # 獲取校驗後的錯誤信息
        print(serializer.errors)
        """打印效果:
            {'read': [ErrorDetail(string='對不起,當前字段必須大於或等於0', code='min_value')]}
        """
        # 獲取校驗後的數據
        print(serializer.validated_data)
        """打印效果:

        from collections import OrderedDict

        OrderedDict([
            ('title', 'python入門'), 
            ('pub_date', datetime.date(2009, 10, 10)), 
            ('read', 10000), 
            ('comment', 300)
        ])
        """

        return JsonResponse({"message": "ok"})
View Code

 

2 反序列化-保存數據

前面的驗證數據成功後,咱們可使用序列化器來完成數據反序列化的過程.這個過程能夠把數據轉成模型類對象.

能夠經過實現create()和update()兩個方法來實現。

在serializers下

 
 
class BookSerializer(serializers.Serializer):
    """圖書數據序列化器""" ...

  def
create(self, validated_data):   """添加數據"""   instance = Book.objects.create(**validated_data)   return instance   def update(self, instance, validated_data):   """修改數據"""   instance.title = validated_data.get("title")   instance.pub_date = validated_data.get("pub_date")   instance.read = validated_data.get("read")    instance.comment = validated_data.get("comment")    # 下面的save是調用了ORM中提供給模型保存數據的save,不是序列化器中的save    instance.save()    return instance

views

from django.views import View
from django.http import JsonResponse
from .serializers import BookSerializer

class BookView(View):
    """使用序列化器完成反序列化和驗證數據功能"""

  # 新增
    def get2(self,request):
        """使用序列化器在反序列化時添加數據"""
        data_dict = {
            "title":"路飛項目實戰1990",
            "pub_date":"2009-10-10",
            "read": 10000,
            "comment": 300
        }

        serializer = BookSerializer(data=data_dict)
        serializer.is_valid(raise_exception=True)
        # 調用序列化器內部保存數據的方法[自動調用create或者update]
        # 在源碼save方法207行中,系統經過判斷初始化序列化器時是否傳遞了instance參數做爲判斷依據,
        # 傳遞了instance,則save方法中調用update
        # 沒有傳遞instance,則save方法中調用create
        serializer.save()


        return JsonResponse({"message":"ok"})
  # 編輯
    def get(self,request):
        """使用圖序列化器在反序列化時修改數據"""
        data_dict = {
            "title":"路飛項目實戰2019",
            "pub_date":"2019-10-10",
            "read": 10000,
            "comment": 300
        }

        pk = 2    # 編輯
        book = Book.objects.get(pk=pk)

        serializer = BookSerializer(instance=book, data=data_dict)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return JsonResponse(serializer.data) 

 

 

 

方法返回一個數據對象實例了

book = serializer.save()

若是建立序列化器對象的時候,沒有傳遞instance實例,則調用save()方法的時候,create()被調用,相反,若是傳遞了instance實例,則調用save()方法的時候,update()被調用。

from .serializers import BookSerializer
data = {'title': 'python入門指南'}
serializer = BookSerializer(data=data)
serializer.is_valid()  # True
serializer.save()  # <BookInfo: python入門指南>

from .models import Book
book = Book.objects.get(id=2)
data = {'title': 'django入門指南'}
serializer = BookSerializer(book, data=data)
serializer.is_valid()  # True
serializer.save()  # <BookInfo: django入門指南>
book.title  # 'django入門指南'

 

3 附加說明

1) 在對序列化器進行save()保存時,能夠額外傳遞數據,這些數據能夠在create()和update()中的validated_data參數獲取到

# request.user 是django中記錄當前登陸用戶的模型對象
serializer.save(自定義字段名=request.user,自定義字段名2=xxx)

2)默認序列化器必須傳遞全部required的字段,不然會拋出驗證異常。可是咱們可使用partial參數來容許部分字段更新

# Update `comment` with partial data
serializer = CommentSerializer(comment, data={'content': u'foo bar'}, partial=True)

 

模型類序列化器

若是咱們想要使用序列化器對應的是Django的模型類,DRF爲咱們提供了ModelSerializer模型類序列化器來幫助咱們快速建立一個Serializer類。

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

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

  • 基於模型類自動爲Serializer生成validators,好比unique_together

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

 建立模型序列化器

from rest_framework import serializers
class BookModelSerializer(serializers.ModelSerializer): # ModelSerializer 模型序列化器

 

定義

好比咱們建立一個BookModelSerializer

class BookModelSerializer(serializers.ModelSerializer):
    """圖書數據序列化器"""
  # 元類
    class Meta:
        model = Book           # 生命當前使用的模型類        
        fields = '__all__'     # 複製全部的字段聲明   
        fields = ['id','title']# 選擇字段
        exclude = ['read']     # 排除字段    

 

  • model 指明參照哪一個模型類

  • fields 指明爲模型類的哪些字段生成

咱們能夠在python manage.py shell中查看自動生成的BookSerializer的具體實現

>>> from booktest.serializers import BookSerializer
>>> serializer = BookSerializer()
>>> serializer
BookSerializer():
    id = IntegerField(label='ID', read_only=True)
    title = CharField(label='名稱', max_length=20)
    pub_date = DateField(allow_null=True, label='發佈日期', required=False)
    read = IntegerField(label='閱讀量', max_value=2147483647, min_value=-2147483648, required=False)
    comment = IntegerField(label='評論量', max_value=2147483647, min_value=-2147483648, required=False)

 

指定字段

1) 使用fields來明確字段,__all__表名包含全部字段,也能夠寫明具體哪些字段,如

class BookSerializer(serializers.ModelSerializer):
    """圖書數據序列化器"""
    class Meta:
        model = Book
        fields = "__all__"

2) 使用exclude能夠明確排除掉哪些字段

class BookSerializer(serializers.ModelSerializer):
    """圖書數據序列化器"""
    class Meta:
        model = Book
        exclude = ('pub_date',)

3) 顯示指明字段,如:

class BookSerializer(serializers.ModelSerializer):
    
    class Meta:
        model = Book
        fields = ('id', 'title', 'comment', 'read')

4) 指明只讀字段

能夠經過read_only_fields指明只讀字段,即僅用於序列化輸出的字段

 

class BookSerializer(serializers.ModelSerializer):
    """圖書序列化器"""
    class Meta:
        model = Book
        fields = ('id', 'title', 'pub_date''read', 'comment')
        read_only_fields = ('id', 'read', 'comment')

 

添加額外參數

咱們可使用extra_kwargs參數爲ModelSerializer添加或修改原有的選項參數

class BookSerializer(serializers.ModelSerializer):
    """圖書序列化器"""
    class Meta:
        model = Book
        fields = ('id', 'title', 'pub_date', 'read', 'comment')
        extra_kwargs = {
            'read': {'min_value': 0, 'required': True},
            'comment': {'min_value': 0, 'required': True},
        }

# BookSerializer():
#    id = IntegerField(label='ID', read_only=True)
#    title = CharField(label='名稱', max_length=20)
#    pub_date = DateField(allow_null=True, label='發佈日期', required=False)
#    read = IntegerField(label='閱讀量', max_value=2147483647, min_value=0, required=True)
#    comment = IntegerField(label='評論量', max_value=2147483647, min_value=0, required=True)

 

 

模型序列化

serializers

"""使用模型類序列化器"""
from rest_framework import serializers
from .models import Book
class BookModelSerializer(serializers.ModelSerializer):
    """字段聲明"""

    """元類"""
    class Meta:
        model = Book # 聲明當前使用的模型類
        fields = "__all__" # 複製全部的字段聲明過來

view

"""使用模型類序列化器"""
from .serializers import BookModelSerializer
from .models import Book
class BookView2(View):
    def get1(self,request):
        """使用模型類序列化器進行序列化"""

        """提供全部的圖書信息"""
        book_list = Book.objects.all()
        serializer = BookModelSerializer(instance=book_list, many=True)
        return JsonResponse(serializer.data,safe=False)

 

模型反序列化

serializers

"""使用模型類序列化器"""
from rest_framework import serializers
from .models import Book
class BookModelSerializer(serializers.ModelSerializer):
    """字段聲明"""

    """元類"""
    class Meta:
        model = Book # 聲明當前使用的模型類
        fields = "__all__" # 複製全部的字段聲明過來
        # fields = ["id","title","read","pub_date"]
        # 額外的字段聲明
        extra_kwargs = {
            "title":{
                "error_messages":{"max_length":"對不起,當前字段不能超過20個字符"}
            }
        }

    # 自定義方法設置校驗方法
    def validate_title(self, data):
        if("oldboy" in data):
            # 跑出錯誤
            raise serializers.ValidationError("對不起,圖書名不能包含oldboy!")

        # 必須在校驗完成之後,返回結果
        return data

    def validate(self, data):
        read = data.get("read")
        comment = data.get("comment")

        if(comment>read):
            raise serializers.ValidationError("對不起,閱讀量不能小於評論量,噴子!")

        return data

 

 

views

"""使用模型類序列化器"""
from .serializers import BookModelSerializer
from .models import Book
class BookView2(View):
    def get(self,request):
        data_dict = {
            "title":"django入門3333",
            "pub_date":"2009-10-10",
            # "read": -10000, # 模擬失敗的狀況
            "read": 100,
            "comment": 30
        }
        serializer = BookModelSerializer(data=data_dict)

        serializer.is_valid(raise_exception=True)

        print(serializer.errors)

        serializer.save()

        return JsonResponse(serializer.validated_data, safe=False)
相關文章
相關標籤/搜索