1. 程式人生 > 實用技巧 >drf序列化元件

drf序列化元件

一. 序列化元件介紹#

Copy
1. 序列化,序列化器會把模型物件轉換成字典,經過response以後變成json字串
2. 反序列化,把客戶端傳送過來的資料,經過request以後變成字典,序列化器可以把字典轉成模型
3. 反序列化,完成資料校驗功能

二. 簡單使用#

步驟:

Copy
1. 寫一個序列化的類,繼承Serializer
2. 在類中寫要序列化的欄位,想序列化哪個欄位,就在類中寫哪個欄位
3. 在檢視類中使用,匯入序列化類把要序列化的物件傳入, 得到序列化物件
4. 可以通過'序列化類的物件.data'獲取序列化後的字典
5. 把字典返回. 提示: 如果不使用rest_framework提供的Response,就得使用JsonResponse

程式碼:

Copy
# models.py
class Book(models.Model):
    name = models.CharField(max_length=32, verbose_name='書名')
    price = models.CharField(max_length=64, verbose_name='價格')
    author = models.CharField(max_length=64, verbose_name='作者')
    publish = models.CharField(max_length=64, verbose_name='出版社'
) # ser.py class BookSerializer(serializers.Serializer): # id=serializers.CharField() name = serializers.CharField() # price=serializers.DecimalField() price = serializers.CharField() author = serializers.CharField() publish = serializers.CharField() # views.py class BookView
(APIView):
def get(self, request, pk): book = Book.objects.filter(id=pk).first() # 將從資料庫中獲取的book物件使用剛剛自定義的序列化類BookSerializer將book進行序列化 book_ser = BookSerializer(book) # 呼叫類的__init__ # book_ser.data # 序列化物件.data就是序列化後的字典 return Response(book_ser.data) # urls.py re_path('books/(?P<pk>\d+)', views.BookView.as_view())

三. 序列化類的欄位型別#

1. 常用欄位型別#

欄位欄位構造方式
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=)

2. 選項引數#

引數名稱作用
max_length 最大長度
min_lenght 最小長度
allow_blank 是否允許為空
trim_whitespace 是否截斷空白字元
max_value 最小值
min_value 最大值

3. 通用引數#

引數名稱說明
read_only 表明該欄位僅用於序列化輸出,預設False
write_only 表明該欄位僅用於反序列化輸入,預設False
required 表明該欄位在反序列化時必須輸入,預設True
default 反序列化時使用的預設值
allow_null 表明該欄位是否允許傳入None,預設False
validators 該欄位使用的驗證器
error_messages 包含錯誤編號與錯誤資訊的字典
label 用於HTML展示API頁面時,顯示的欄位名稱
help_text 用於HTML展示API頁面時,顯示的欄位幫助提示資訊

四. 序列化元件修改資料#

步驟:

Copy
1. 寫一個序列化的類,繼承Serializer
2. 在類中寫要反序列化的欄位,想反序列化哪個欄位,就在類中寫哪個欄位,欄位的屬性
    max_length	最大長度
    min_lenght	最小長度
    allow_blank	是否允許為空
    trim_whitespace	是否截斷空白字元
    max_value	最小值
    min_value	最大值
3. 在檢視類中使用,匯入自定義的繼承繼承Serializer類的序列化類,把要要修改的物件傳入,得到序列序列化物件.
    boo_ser=BookSerializer(book,request.data)
    boo_ser=BookSerializer(instance=book,data=request.data)
4. 資料校驗 if boo_ser.is_valid()
5. 如果校驗通過,就儲存
    boo_ser.save()  # 注意不是book.save()
6. 如果不通過,返回restful規範的返回結果
7. 如果欄位的校驗規則不夠,可以寫鉤子函式(區域性和全域性)
    # 區域性鉤子
    def validate_price(self, data):  # validate_欄位名  接收一個引數
    # 如果價格小於10,就校驗不通過
    # print(type(data))
    # print(data)
    if float(data) > 10:
        return data
    else:
        # 校驗失敗,拋異常
        raise ValidationError('價格太低')

    
    # 全域性鉤子
    def validate(self, validate_data):  # 全域性鉤子
        # print(validate_data)
        author = validate_data.get('author')
        publish = validate_data.get('publish')
        if author == publish:
            raise ValidationError('作者名字跟出版社一樣')
        else:
            return validate_data


8.也可以可以使用欄位的author = serializers.CharField(validators=[check_author]) ,來校驗

    # 定義一個校驗函式
    def check_author(data):
        if data.startswith('sb'):
            raise ValidationError('作者名字不能以sb開頭')
        else:
            return data
    # 在序列化欄位中配置引數:validators=[check_author]

程式碼:

Copy
# models.py
class Book(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=5, decimal_places=2)
    author = models.CharField(max_length=32)
    publish = models.CharField(max_length=32)


# ser.py
# from rest_framework.serializers import Serializer  # Serializer就是一個序列化類
from rest_framework import serializers
from rest_framework.exceptions import ValidationError


def check_author(data):
    if data.startswith('sb'):
        raise ValidationError('作者名字不能以sb開頭')
    else:
        return data


class BookSerializer(serializers.Serializer):
    # id=serializers.CharField()
    name = serializers.CharField(max_length=16, min_length=4)
    # price=serializers.DecimalField()
    price = serializers.CharField()
    author = serializers.CharField(validators=[check_author])  # validators=[] 列表中寫函式記憶體地址
    publish = serializers.CharField()

    def validate_price(self, data):  # validate_欄位名  接收一個引數
        # 如果價格小於10,就校驗不通過
        # print(type(data))
        # print(data)
        if float(data) > 10:
            return data
        else:
            # 校驗失敗,拋異常
            raise ValidationError('價格太低')

    def validate(self, validate_data):  # 全域性鉤子
        print(validate_data)
        author = validate_data.get('author')
        publish = validate_data.get('publish')
        if author == publish:
            raise ValidationError('作者名字跟出版社一樣')
        else:
            return validate_data

    def update(self, instance, validated_data):
        # instance是book這個物件
        # validated_data是校驗後的資料
        instance.name = validated_data.get('name')
        instance.price = validated_data.get('price')
        instance.author = validated_data.get('author')
        instance.publish = validated_data.get('publish')
        instance.save()  # book.save()   django 的orm提供的
        return instance


# views.py
class BookView(APIView):
    def get(self, request, pk):
        book = Book.objects.filter(id=pk).first()
        # 用一個類,毫無疑問,一定要例項化
        # 要序列化誰,就把誰傳過來
        book_ser = BookSerializer(book)  # 呼叫類的__init__
        # book_ser.data   序列化物件.data就是序列化後的字典
        return Response(book_ser.data)
        # return JsonResponse(book_ser.data)

    def put(self, request, pk):
        response_msg = {'status': 100, 'msg': '成功'}
        # 找到這個物件
        book = Book.objects.filter(id=pk).first()
        # 得到一個序列化類的物件
        # boo_ser=BookSerializer(book,request.data)
        boo_ser = BookSerializer(instance=book, data=request.data)

        # 要資料驗證(回想form表單的驗證)
        if boo_ser.is_valid():  # 返回True表示驗證通過
            boo_ser.save()  # 報錯
            response_msg['data'] = boo_ser.data
        else:
            response_msg['status'] = 101
            response_msg['msg'] = '資料校驗失敗'
            response_msg['data'] = boo_ser.errors

        return Response(response_msg)


# urls.py
re_path('books/(?P<pk>\d+)', views.BookView.as_view()),

五. read_only和write_only#

Copy
# 常用
read_only	    表明該欄位僅用於序列化輸出,預設False,如果設定成True,postman中可以看到該欄位,修改時,不需要傳該欄位
write_only	    表明該欄位僅用於反序列化輸入,預設False,如果設定成True,postman中看不到該欄位,修改時,該欄位需要傳

# 瞭解
required	    表明該欄位在反序列化時必須輸入,預設True
default	        反序列化時使用的預設值
allow_null	    表明該欄位是否允許傳入None,預設False
validators	    該欄位使用的驗證器
error_messages	包含錯誤編號與錯誤資訊的字典

六. 查詢所有#

Copy
# views.py
class BooksView(APIView):
    def get(self, request):
        response_msg = {'status': 100, 'msg': '成功'}
        books = Book.objects.all()
        book_ser = BookSerializer(books, many=True)  # 序列化多條,如果序列化一條,不需要寫
        response_msg['data'] = book_ser.data
        return Response(response_msg)


# urls.py
path('books/', views.BooksView.as_view())

七. 新增資料#

Copy
# views.py
class BooksView(APIView):
    # 新增
    def post(self,request):
        response_msg = {'status': 100, 'msg': '成功'}
        #修改才有instance,新增沒有instance,只有data
        book_ser = BookSerializer(data=request.data)
        # book_ser = BookSerializer(request.data)  # 這個按位置傳request.data會給instance,就報錯了
        # 校驗欄位
        if book_ser.is_valid():
            book_ser.save()
            response_msg['data']=book_ser.data
        else:
            response_msg['status']=102
            response_msg['msg']='資料校驗失敗'
            response_msg['data']=book_ser.errors
        return Response(response_msg)
    
#ser.py 序列化類重寫create方法
    def create(self, validated_data):
        instance=Book.objects.create(**validated_data)
        return instance
    
# urls.py
path('books/', views.BooksView.as_view()),

八. 刪除一個數據#

Copy
# views.py
class BookView(APIView):
    def delete(self,request,pk):
        ret=Book.objects.filter(pk=pk).delete()
        return Response({'status':100,'msg':'刪除成功'})
    
# urls.py
re_path('books/(?P<pk>\d+)', views.BookView.as_view()),

九. 模型類序列化器#

Copy
class BookModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book  # 對應上models.py中的模型
        fields = '__all__'
        # fields=('name','price','id','author') # 只序列化指定的欄位
        # exclude=('name',) # 跟fields不能都寫,寫誰,就表示排除誰
        # read_only_fields=('price',)
        # write_only_fields=('id',) # 棄用了,使用extra_kwargs
        extra_kwargs = {  # 類似於這種形式name=serializers.CharField(max_length=16,min_length=4)
            'price': {'write_only': True},
        }

# 其他使用一模一樣
# 不需要重寫create和updata方法了

十. 原始碼分析#

1. many=True#

Copy
# 序列化多條,需要傳many=True

book_ser=BookModelSerializer(books,many=True)
book_one_ser=BookModelSerializer(book)
print(type(book_ser))
# <class 'rest_framework.serializers.ListSerializer'>
print(type(book_one_ser))
# <class 'app01.ser.BookModelSerializer'>

# 物件的生成--》先呼叫類的__new__方法,生成空物件
# 物件=類名(name=lqz),觸發類的__init__()
# 類的__new__方法控制物件的生成


def __new__(cls, *args, **kwargs):
    if kwargs.pop('many', False):
        return cls.many_init(*args, **kwargs)
    # 沒有傳many=True,走下面,正常的物件例項化
    return super().__new__(cls, *args, **kwargs)

2. Serializer高階用法#

1). source#

Copy
# 1. 可以改欄位名字  
    xxx = serializers.CharField(source='title')
# 2. 可以.跨表
    publish = serializers.CharField(source='publish.email')
# 3. 可以執行方法
    pub_date= serializers.CharField(source='test')  # 注: test是Book表模型中的方法

2). SerializerMethodField#

Copy
authors=serializers.SerializerMethodField()  # 它需要有個配套方法,方法名叫get_欄位名,返回值就是要顯示的東西
def get_authors(self,instance):
    # book物件
    authors=instance.authors.all()  # 取出所有作者
    ll=[]
    for author in authors:
        ll.append({'name':author.name,'age':author.age})
        return ll

十一. 補充: is_valid的另一種用法#

is_valid()方法還可以在驗證失敗時丟擲異常serializers.ValidationError,可以通過傳遞raise_exception = True引數開啟,REST框架接收到此異常,會向前端返回HTTP 400錯誤的請求響應。

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