1. 程式人生 > >如何使用DRF的序列化器之反序列化

如何使用DRF的序列化器之反序列化

驗證資料:使用序列化器進行反序列化時,需要對資料進行驗證後,才能獲取驗證成功的資料或儲存成模型類物件。

在獲取反序列化的資料前,必須呼叫is_valid()方法進行驗證,驗證成功返回True,否則返回False。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)

 驗證成功,可以通過序列化器物件的validated_data屬性獲取資料。serializer.validated_data為OrderedDict物件 如OrderedDict([('btitle', 'python進階')])

通過構造序列化器物件,並將要反序列化的資料傳遞給data構造引數,進而進行驗證

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

data = {'btitle': 'python'}
serializer = BookInfoSerializer(data=data)
serializer.is_valid()  # True
serializer.errors  # {}

serializer.validated_data  #  OrderedDict([('btitle', 'python')])

在定義序列化器時,指明每個欄位的序列化型別和選項引數,本身就是一種驗證行為。

序列化器還提供了驗證函式用來自定義驗證:

def validate_屬性(self, value): 用來針對單個欄位進行驗證

class BookInfoSerializer(serializers.Serializer):
    """圖書資料序列化器"""
    ...

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

def validate(self, attrs):用來同時對多個欄位進行驗證

class BookInfoSerializer(serializers.Serializer):
    """圖書資料序列化器"""
    ...

    def validate(self, attrs):
        bread = attrs['bread']
        bcomment = attrs['bcomment']
        if bread < bcomment:
            raise serializers.ValidationError('閱讀量小於評論量')
        return attrs

驗證函式一定要將驗證後的資料return!

validators選項,在欄位中新增validators選項引數,可以補充驗證行為

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

class BookInfoSerializer(serializers.Serializer):
    """圖書資料序列化器"""
    id = serializers.IntegerField(label='ID', read_only=True)
    btitle = serializers.CharField(label='名稱', max_length=20, validators=[about_django])
    bpub_date = serializers.DateField(label='釋出日期', required=False)

儲存資料:如果在驗證成功後,想要基於validated_data完成資料物件的建立及儲存到資料庫,可以通過實現create()和update()兩個方法來實現。

 在序列化器類中新增creat和update方法

class BookInfoSerializer(serializers.Serializer):
    """圖書資料序列化器"""
    ...

    def create(self, validated_data):
        """新建"""
        return BookInfo.objects.create(**validated_data)

    def update(self, instance, validated_data):
        """更新,instance為要更新的物件例項"""
        instance.btitle = validated_data.get('btitle', instance.btitle)
        instance.bpub_date = validated_data.get('bpub_date', instance.bpub_date)
        instance.bread = validated_data.get('bread', instance.bread)
        instance.bcomment = validated_data.get('bcomment', instance.bcomment)
        instance.save()
        return instance

實現了上述兩個方法後,在反序列化資料的時候,就可以通過save()方法返回一個數據物件例項了 

book = serializer.save()

 如果建立序列化器物件的時候,沒有傳遞instance例項,則呼叫save()方法的時候,create()被呼叫,相反,如果傳遞了instance例項,則呼叫save()方法的時候,update()被呼叫。不需要手動呼叫validated_date。

from db.serializers import BookInfoSerializer
data = {'btitle': 'python進階'}
serializer = BookInfoSerializer(data=data)
serializer.is_valid()  # True
serializer.save()  # <BookInfo: python進階>

from db.models import BookInfo
book = BookInfo.objects.get(id=2)
data = {'btitle': 'python核心程式設計'}
serializer = BookInfoSerializer(book, data=data)
serializer.is_valid()  # True
serializer.save()  # <BookInfo: python核心程式設計>
book.btitle  # 'python核心程式設計'

兩點說明:

1) 在對序列化器進行save()儲存時,可以額外傳遞資料,這些資料可以在create()和update()中的validated_data引數獲取到。

serializer.save(owner=request.user)

2)預設序列化器必須傳遞所有required的欄位,否則會丟擲驗證異常。但是我們可以使用partial引數來允許部分欄位更新,建立serializer物件時,傳入partial引數,則所有欄位的required選項失效。

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

反序列化幫我們封裝了將字典轉換為物件的過程。

# 建立物件
serializer = BookInfoSerializer(data=data)
serializer.save()
# 更新物件
serializer = BookInfoSerializer(book, data=data)
serializer.save()