drf序列化器之反序列化的數據驗證


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

在獲取反序列化的客戶端數據前,必須在視圖中調用序列化對象的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頁面時,顯示的字段幫助提示信息


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM