1. 程式人生 > >Serializer序列化器的定義與使用

Serializer序列化器的定義與使用

序列化器的作用:

  1. 對資料進行校驗
  2. 對資料物件進行轉換(資料模型類和dict等資料間的轉換)

定義方法

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

例如,我們已有了一個數據庫模型類BookInfo

class BookInfo(models.Model):
    btitle = models.CharField(max_length=20, verbose_name='名稱')
    bpub_date = models.DateField(verbose_name='釋出日期', null=True)
    bread = models.IntegerField(default=0, verbose_name='閱讀量')
    bcomment = models.IntegerField(default=0, verbose_name='評論量')
    image = models.ImageField(upload_to='booktest', verbose_name='圖片', null=True)

我們想為這個模型類提供一個序列化器,可以定義如下:

​
class BookInfoSerializer(serializers.Serializer):
    """圖書資料序列化器"""
    id = serializers.IntegerField(label='ID', read_only=True)
    btitle = serializers.CharField(label='名稱', max_length=20)
    bpub_date = serializers.DateField(label='釋出日期', required=False)
    bread = serializers.IntegerField(label='閱讀量', required=False)
    bcomment = serializers.IntegerField(label='評論量', required=False)
    image = serializers.ImageField(label='圖片', required=False)

​

建立序列化器物件

定義好Serializer類後,就可以建立Serializer物件了。

Serializer的構造方法為:

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

1)用於序列化時,將模型類物件傳入instance引數

2)用於反序列化時,將要被反序列化的資料傳入data引數

3 ) 同時傳入instance引數和data引數,使用傳入的data資料對模型類物件例項進行修改

一:序列化

1:基本使用

還是以上面的圖書模型類資料為例

可在django shell中來簡單的使用序列化器來測試效果(注意修改資料模型類或序列化器等檔案後要重啟shell)

python manager.py shell

1) 先查詢出一個圖書物件

from booktest.models import BookInfo

book = BookInfo.objects.get(id=1)

2) 構造序列化器物件

from booktest.serializers import BookInfoSerializer

serializer = BookInfoSerializer(book)

3)獲取序列化資料

通過data屬性可以獲取序列化後的資料

serializer.data

4)如果要被序列化的是包含多條資料的查詢集QuerySet,可以通過新增many=True引數補充說明 

book_qs = BookInfo.objects.all()
serializer = BookInfoSerializer(book_qs, many=True)
serializer.data

測試程式碼:

>>> from booktest.models import BookInfo
>>> from booktest.serializers import BookInfoSerializer
>>> book = BookInfo.objects.get(id=1)
>>> serializer = BookInfoSerializer(instance=book)
>>> serializer.data
{'btitle': '射鵰英雄傳', 'id': 1, 'bpub_date': '1980-05-01', 'bread': 12, 'bcomment': 34}
>>> 

 

2.關聯物件巢狀序列化

如果需要序列化的資料中包含有其他關聯物件,則對關聯物件資料的序列化需要指明。

例如,在定義英雄資料的序列化器時,外來鍵hbook(即所屬的圖書)欄位如何序列化?

我們先定義HeroInfoSerialzier除外來鍵欄位外的其他部分

class HeroInfoSerializer(serializers.Serializer):
    """英雄資料序列化器"""
    GENDER_CHOICES = (
        (0, 'male'),
        (1, 'female')
    )
    id = serializers.IntegerField(label='ID', read_only=True)
    hname = serializers.CharField(label='名字', max_length=20)
    hgender = serializers.ChoiceField(choices=GENDER_CHOICES, label='性別', required=False)
    hcomment = serializers.CharField(label='描述資訊', max_length=200, required=False, allow_null=True)

對於關聯欄位,可以採用以下幾種方式: 

1) PrimaryKeyRelatedField   : 此欄位將被序列化為關聯物件的主鍵。

hbook = serializers.PrimaryKeyRelatedField(label='圖書', read_only=True)
或
hbook = serializers.PrimaryKeyRelatedField(label='圖書', queryset=BookInfo.objects.all())

指明欄位時需要包含read_only=True或者queryset引數:

  • 包含read_only=True引數時,該欄位將不能用作反序列化使用
  • 包含queryset引數時,將被用作反序列化時引數校驗使用

使用效果:

from booktest.serializers import HeroInfoSerializer
from booktest.models import HeroInfo
hero = HeroInfo.objects.get(id=6)
serializer = HeroInfoSerializer(hero)
serializer.data
# {'id': 6, 'hname': '喬峰', 'hgender': 1, 'hcomment': '降龍十八掌', 'hbook': 2}

2) StringRelatedField

此欄位將被序列化為關聯物件的字串表示方式(即__str__方法的返回值)

hbook = serializers.StringRelatedField(label='圖書')

3)使用關聯物件的序列化器 

hbook = BookInfoSerializer()

 3. many引數

如果關聯的物件資料不是隻有一個,而是包含多個數據,如想序列化圖書BookInfo資料,每個BookInfo物件關聯的英雄HeroInfo物件可能有多個,此時關聯欄位型別的指明仍可使用上述幾種方式,只是在宣告關聯欄位時,多補充一個many=True引數即可。

此處僅拿PrimaryKeyRelatedField型別來舉例,其他相同。

在BookInfoSerializer中新增關聯欄位:

class BookInfoSerializer(serializers.Serializer):
    """圖書資料序列化器"""
    id = serializers.IntegerField(label='ID', read_only=True)
    btitle = serializers.CharField(label='名稱', max_length=20)
    bpub_date = serializers.DateField(label='釋出日期', required=False)
    bread = serializers.IntegerField(label='閱讀量', required=False)
    bcomment = serializers.IntegerField(label='評論量', required=False)
    image = serializers.ImageField(label='圖片', required=False)
    heroinfo_set = serializers.PrimaryKeyRelatedField(read_only=True, many=True)  # 新增
使用效果:

使用效果:

from booktest.serializers import BookInfoSerializer
from booktest.models import BookInfo
book = BookInfo.objects.get(id=2)
serializer = BookInfoSerializer(book)
serializer.data
# {'id': 2, 'btitle': '天龍八部', 'bpub_date': '1986-07-24', 'bread': 36, 'bcomment': 40, 'image': None, 'heroinfo_set': [6,8, 9]}

 

 

二:反序列化

1.驗證

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

在獲取反序列化的資料前,必須呼叫is_valid()方法進行驗證,驗證成功返回True,否則返回False

驗證的方式:
        0.型別+選項:----------------------------------------及定義序列化器時,序列化器欄位本身型別和選項提供的驗證
        1.validate_屬性名稱(self,value):---------------驗證指定的屬性值是否合法

# 對某個屬性欄位進行補充驗證
    def validate_btitle(self, value):
        if 'django' not in value.lower():
            raise serializers.ValidationError('圖書不是關於django的')
        return value


        2.validate(self,values):---------------------------驗證多個屬性

# 多個欄位之間的比較驗證
    def validate(self, attrs):
        bread = attrs.get('bread')
        bcomment = attrs.get('bcomment')
        if all ([bread, bcomment]):
            if bread < bcomment:
                raise serializers.ValidationError('閱讀量不能小於評論量')
        return attrs


        3.validators=[方法]:--------------------------------針對屬性進行驗證(在序列化器外部定義好一個函式,載入到序列化器的欄位上)


# 對某個欄位的選項進行驗證
def pub_date(value):
    if value < date(2010, 1, 1):
        raise serializers.ValidationError('日期必須實在2010-1-1日之後')
    return value


class BookInfoSerializer(serializers.Serializer):
    ...
    bpub_date = serializers.DateField(label='釋出日期', validators=[pub_date])
    ...

2.呼叫

        serialiazer=模型類Serializer(data=字典)
        serializer.is_valid()-----------進行驗證,如果成功則返回True,失敗則返回False
        serialiser.error-----------------錯誤資訊,型別為字典

3.儲存

   1.定義序列化器,增加create()、update()方法
    2.呼叫:serializer.save()

PS:如果是建立:序列化器類(data=***)===>serializer.save()===>呼叫create()
        如果是修改:序列化器類(模型類物件,data=***)===>serializer.save()=====>呼叫update()
        如果是部分修改:序列化器類(模型類物件,data=***,partial=True)===>serializer.save()====>呼叫update()
        跳過了必填的檢查