使用序列化器進行反序列化時,需要對數據進行驗證后,才能獲取驗證成功的數據或保存成模型類對象。
在獲取反序列化的客戶端數據前,必須在視圖中調用序列化對象的is_valid()方法,序列化器內部是在is_valid方法內部調用驗證選項和驗證方法進行驗證,驗證成功返回True,否則返回False。
驗證失敗,可以通過序列化器對象的errors屬性獲取錯誤信息,返回字典,包含了字段和字段的錯誤提示。如果是非字段錯誤,可以通過修改REST framework配置中的NON_FIELD_ERRORS_KEY來控制錯誤字典中的鍵名。
驗證成功,可以通過序列化器對象的validated_data屬性獲取數據。
在定義序列化器時,指明每個字段的序列化類型和選項參數,本身就是一種驗證行為。
1、准備工作
注冊一個圖書app及圖書表模型
python manage.py startapp unsers
在配置文件setting.py中注冊子應用
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework', # 把drf框架注冊到django項目中
'unsers', # 演示反序列化
]
注釋csrf校驗,因為提交數據涉及到post方法提交數據,把settings.py中的中間件的csrf暫時關閉
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
# 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
創建表模型
from django.db import models
# Create your models here.
class BookInfo(models.Model):
"""圖書信息"""
title = models.CharField(max_length=20, verbose_name='標題')
pub_date = models.DateField(verbose_name='發布日期')
# 設置存儲文件的子目錄為avatar,總目錄不寫的話是在settings中配置,不填則沒有
image = models.ImageField(upload_to="avatar", verbose_name='圖書封面')
price = models.DecimalField(max_digits=8, decimal_places=2, verbose_name="價格")
read = models.IntegerField(verbose_name='閱讀量')
comment = models.IntegerField(verbose_name='評論量')
class Meta:
# db_table = "表名"
db_table = "tb_book_info"
verbose_name = "圖書"
verbose_name_plural = verbose_name
注意:因為當前模型中, 設置到圖片上傳處理,運行起來后會有提示,所以需要安裝PIL庫
pip3 install Pillow
執行數據遷移
python3 manage.py makemigrations
python3 manage.py migrate
2、字段驗證
經過上面的准備工作,接下來就可以給圖書信息增加圖書的功能,需要對來自客戶端的數據進行處理,例如,驗證和保存到數據庫中。此時,就可以使用序列化器的反序列化器,接下來,定義一個圖書的序列化器,此序列化器主要用於反序列化器階段,在unsers子應用,創建serializers.py,代碼如下
from rest_framework import serializers
class BookInfoSerializer(serializers.Serializer):
# 這里聲明的字段用於進行反序列化器
# 字段名 = serializers.字段類型(驗證選項)
# read_only=True,設置id為只讀字段,當字段設置為read_only為True,則當前字段只會在序列化階段使用
id = serializers.IntegerField(read_only=True)
title = serializers.CharField(required=True, min_length=1, max_length=20, label="標題", help_text="標題", error_messages={
"required": "標題不能為空!",
"max_length": "標題不能超過6個字符",
})
# required=True 當前字段必填
# write_only=True 表示當前字段只會在反序列化階段使用,客戶端提交數據的時候使用,不會提供給客戶端
pub_date = serializers.DateField(required=True,label="發布日期", help_text="發布日期")
price = serializers.DecimalField(max_digits=8, decimal_places=2, required=True, label="價格", help_text="價格")
read = serializers.IntegerField(min_value=0, default=0, label="閱讀量", help_text="閱讀量")
comment = serializers.IntegerField(min_value=0, default=0, label="評論量", help_text="評論量")
通過構造序列化器對象,並將要反序列化的數據傳遞給data構造參數,進而進行驗證,編寫視圖類如下
# Create your views here.
from django.views import View
from .models import BookInfo
from django.http.response import JsonResponse
from .serializers import BookInfoSerializer
class BookInfoView(View):
def post(self, request):
"""反序列化,驗證和添加數據"""
# 接收並實例化序列化器對象
serializer = BookInfoSerializer(data=request.POST)
# 啟動驗證
# is_valid 有個可選參數raise_exception,用於顯示序列化器拋出的異常,直接終止視圖代碼的執行
# 如果設置了raise_exception=True,則下面的18~21行代碼,就不要開發者自己編寫,系統會自動根據請求的方式自動返回錯誤給客戶端。
# 如果是ajax請求,則自動返回json格式的錯誤信息
# 如果是表單請求,則自動返回html格式的錯誤信息
result = serializer.is_valid()
# result = serializer.is_valid(raise_exception=True)
print(result) # 驗證結果,True表示驗證通過了,開發時一般不需要接收
if not result:
# 當驗證失敗,則錯誤信息屬性就有內容
print(serializer.errors)
return JsonResponse(serializer.errors)
else:
# 獲取驗證完成后的客戶端數據 如果驗證失敗,則vcalidated_data是空字典
print(serializer.validated_data)
# 把數據保存到數據庫中
instance = BookInfo.objects.create(**serializer.validated_data)
# instance = serializer.create(serializer.validated_data)
# 序列化器實例化時,如果有save參數,則save相當於update,否則就是create
# instance = serializer.save()
# 返回結果,也是需要使用序列化進行轉換的
serializer = BookInfoSerializer(instance=instance)
return JsonResponse(serializer.data)
注冊url
from django.urls import path
from . import views
urlpatterns = [
path("books/", views.BookInfoView.as_view()),
]
利用postman測試向此接口提交數據

此時查看數據庫中的記錄,已經成功被寫入
3、validate_字段名驗證
對<field_name>字段進行驗證,在序列化器中編寫如下內容:
def validate_title(self, data):
# 驗證單個字段時,方法名必須固定為validate_字段,這里的data代表的就是字段值,
if "測試" in data:
"""拋出異常"""
raise serializers.ValidationError("對不起,當前標題不能出現關鍵字")
# 驗證方法必須要有返回值,這里的返回值將會被填寫到 serailzier對象的validated_data里面
return data # 驗證通過以后,必須要返回驗證的結果數據,否則序列化器的validated_data無法得到當前字段的結果
利用postman測試向此接口提交數據

4、validate驗證
在序列化器中需要同時對多個字段進行比較驗證時,可以定義validate方法來驗證
def validate(self, data):
"""驗證多個字段時,方法名必須為validate,
參數data代表了所有字段的數據值,其實就是視圖代碼中實例化序列化器對象時的data參數
開發中,類似 密碼和確認密碼,此時這2個字段,必須進行比較才能通過驗證
"""
print(data)
# 例如,我們要求圖書的評論必須比閱讀量要少
read = data.get("read")
comment = data.get("comment")
if read < comment:
raise serializers.ValidationError("對不起,閱讀量必須比評論量大")
# 驗證密碼和確認密碼
# 驗證方法必須要有返回值
return data
利用postman測試向此接口提交數據

5、validators驗證器驗證
驗證器類似於驗證方法,但是驗證方法只屬於當前序列化器,如果有多個序列化器共用同樣的驗證功能,則可以把驗證代碼分離到序列化器外部,作為一個普通函數,由validators加載到序列化器中使用。
在字段中添加validators選項參數,也可以補充驗證行為,如下
# 在序列化器的外面聲明一個驗證函數
def check_price(data): # data代表要驗證的數據
if data < 0:
raise serializers.ValidationError("對不起,價格不能出現負數")
# 驗證函數也必須把數據返回
return data
...
class BookInfoSerializer(serializers.Serializer):
...
# 調用驗證器validators,這里的參數是一個列表,列表的成員是函數,函數名不能加引號
# price = serializers.DecimalField(required=True, max_digits=8, decimal_places=2)
price = serializers.DecimalField(required=True, max_digits=8, decimal_places=2, validators=[check_price])
利用postman測試向此接口提交數據

6、小結
is_valid實際上內部執行了三種不同的驗證方式:
- 先執行了字段內置的驗證選項
- 在執行了
validators自定義選項 - 最后執行了
validate自定義驗證方法[包含了validate_<字段>, validate]
附:常用字段和參數
常用字段類型:
| 字段 | 字段構造方式 |
|---|---|
| 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頁面時,顯示的字段幫助提示信息 |
