Serializers

Serializers

序列化程序允許將復(fù)雜數(shù)據(jù)(如 querysets 和 modle 實(shí)例)轉(zhuǎn)換為原生的Python數(shù)據(jù)類型膛锭,然后可以將其簡(jiǎn)單地呈現(xiàn)為JSON,XML或其他內(nèi)容類型蝗岖。 序列化器還提供反序列化,允許解析的數(shù)據(jù)在首次驗(yàn)證傳入數(shù)據(jù)后轉(zhuǎn)換成復(fù)雜類型榔至。

REST框架中的序列化工作與Django的Form和ModelForm類非常相似抵赢。 我們提供了一個(gè)Serializer類,它為您提供了強(qiáng)大的通用方法來(lái)控制響應(yīng)的輸出唧取,以及一個(gè)ModelSerializer類铅鲤,它為創(chuàng)建用于處理模型實(shí)例和查詢結(jié)果的序列化程序提供了有用的快捷方式。

Declaring Serializers(聲明序列化)

讓我們從創(chuàng)建一個(gè)簡(jiǎn)單的對(duì)象開始枫弟,我們可以使用這個(gè)例子:

from datetime import datetime

class Comment(object):
    def __init__(self, email, content, created=None):
        self.email = email
        self.content = content
        self.created = created or datetime.now()

comment = Comment(email='leila@example.com', content='foo bar')

我們將聲明一個(gè)序列化器邢享,我們可以使用它來(lái)序列化和反序列化與Comment對(duì)象相對(duì)應(yīng)的數(shù)據(jù)。

聲明一個(gè)序列化器看起來(lái)非常類似于聲明一個(gè)表單:

from rest_framework import serializers

class CommentSerializer(serializers.Serializer):
    email = serializers.EmailField()
    content = serializers.CharField(max_length=200)
    created = serializers.DateTimeField()

Serializing objects

我們可以用CommentSerializer 來(lái)序列化一個(gè)comment或者comment的列表淡诗。再一次說骇塘,使用Serializer類和使用Form類很類似。

serializer = CommentSerializer(comment)
serializer.data
# {'email': 'leila@example.com', 'content': 'foo bar', 'created': '2016-01-27T15:17:10.375877'}

此時(shí)韩容,我們將模型實(shí)例轉(zhuǎn)換為Python原生數(shù)據(jù)類型款违。 要完成序列化過程,我們將數(shù)據(jù)呈現(xiàn)給json群凶。

from rest_framework.renderers import JSONRenderer

json = JSONRenderer().render(serializer.data)
json
# b'{"email":"leila@example.com","content":"foo bar","created":"2016-01-27T15:17:10.375877"}'

Deserializing objects

反序列化也是類似的插爹,首先我們將一個(gè)流解析為Python原生數(shù)據(jù)類型...

from django.utils.six import BytesIO
from rest_framework.parsers import JSONParser

stream = BytesIO(json)
data = JSONParser().parse(stream)

...然后我們將這些原生數(shù)據(jù)類型恢復(fù)到已驗(yàn)證數(shù)據(jù)的字典中。

serializer = CommentSerializer(data=data)
serializer.is_valid()
# True
serializer.validated_data
# {'content': 'foo bar', 'email': 'leila@example.com', 'created': datetime.datetime(2012, 08, 22, 16, 20, 09, 822243)}

Saving instances

如果我們希望能夠根據(jù)驗(yàn)證的數(shù)據(jù)返回完整的對(duì)象實(shí)例,我們需要實(shí)現(xiàn).create()和update()方法之一或兩者赠尾。 例如:

class CommentSerializer(serializers.Serializer):
    email = serializers.EmailField()
    content = serializers.CharField(max_length=200)
    created = serializers.DateTimeField()

    def create(self, validated_data):
        return Comment(**validated_data)

    def update(self, instance, validated_data):
        instance.email = validated_data.get('email', instance.email)
        instance.content = validated_data.get('content', instance.content)
        instance.created = validated_data.get('created', instance.created)
        return instance

如果你的對(duì)象實(shí)例對(duì)應(yīng)于Django模型力穗,您還需要確保這些方法將對(duì)象保存到數(shù)據(jù)庫(kù)。 例如气嫁,如果Comment是Django模型睛廊,那么這些方法可能如下所示:

def create(self, validated_data):
        return Comment.objects.create(**validated_data)

    def update(self, instance, validated_data):
        instance.email = validated_data.get('email', instance.email)
        instance.content = validated_data.get('content', instance.content)
        instance.created = validated_data.get('created', instance.created)
        instance.save()
        return instance

現(xiàn)在反序列化數(shù)據(jù)時(shí),我們可以根據(jù)驗(yàn)證的數(shù)據(jù)調(diào)用.save()返回一個(gè)對(duì)象實(shí)例杉编。
comment = serializer.save()
調(diào)用.save()將創(chuàng)建一個(gè)新的實(shí)例或更新一個(gè)現(xiàn)有的實(shí)例,具體取決于在實(shí)例化序列化器類時(shí)是否傳遞了一個(gè)現(xiàn)有的實(shí)例:

# .save() will create a new instance.
serializer = CommentSerializer(data=data)

# .save() will update the existing `comment` instance.
serializer = CommentSerializer(comment, data=data)

.create()和.update()方法都是可選的咆霜。 你可以根據(jù)你的Serializer類的用例來(lái)實(shí)現(xiàn)它們之一邓馒,一個(gè)或兩者。

Passing additional attributes to .save()

有時(shí)你會(huì)希望你的視圖代碼能夠在保存實(shí)例時(shí)注入額外的數(shù)據(jù)蛾坯。 此附加數(shù)據(jù)可能包括當(dāng)前用戶光酣,當(dāng)前時(shí)間或不是請(qǐng)求數(shù)據(jù)一部分的其他信息。

您可以通過在調(diào)用.save()時(shí)包含其他關(guān)鍵字參數(shù)來(lái)執(zhí)行此操作脉课。 例如:

serializer.save(owner=request.user)

當(dāng)調(diào)用.create()或.update()時(shí)救军,任何其他關(guān)鍵字參數(shù)將被包含在validated_data參數(shù)中。

Overriding .save() directly.

在某些情況下倘零,.create()和.update()方法名稱可能無(wú)意義唱遭。 例如,在聯(lián)系表單中呈驶,我們可能不會(huì)創(chuàng)建新的實(shí)例拷泽,而是發(fā)送電子郵件或其他消息。

在這些情況下袖瞻,你可以直接選擇覆蓋.save()司致,因?yàn)樗勺x和有意義。
例如:

class ContactForm(serializers.Serializer):
    email = serializers.EmailField()
    message = serializers.CharField()

    def save(self):
        email = self.validated_data['email']
        message = self.validated_data['message']
        send_email(from=email, message=message)

請(qǐng)注意聋迎,在上述情況下脂矫,我們現(xiàn)在必須直接訪問serializer_date屬性。

Validation

反序列化數(shù)據(jù)時(shí)霉晕,你始終需要在嘗試訪問經(jīng)過驗(yàn)證的數(shù)據(jù)之前調(diào)用is_valid()或保存對(duì)象實(shí)例庭再。 如果發(fā)生任何驗(yàn)證錯(cuò)誤,則.errors屬性將包含表示生成的錯(cuò)誤消息的字典牺堰。 例如:

serializer = CommentSerializer(data={'email': 'foobar', 'content': 'baz'})
serializer.is_valid()
# False
serializer.errors
# {'email': [u'Enter a valid e-mail address.'], 'created': [u'This field is required.']}

字典中的每個(gè)鍵都將是字段名稱佩微,值將是與該字段對(duì)應(yīng)的任何錯(cuò)誤消息的字符串列表。 non_field_errors鍵也可能存在萌焰,并將列出任何一般驗(yàn)證錯(cuò)誤哺眯。 可以使用NON_FIELD_ERRORS_KEY REST框架設(shè)置來(lái)定制non_field_errors鍵的名稱。

當(dāng)對(duì)項(xiàng)目列表進(jìn)行反序列化時(shí)扒俯,將作為代表每個(gè)反序列化項(xiàng)目的字典列表返回錯(cuò)誤奶卓。

Raising an exception on invalid data

.is_valid()方法接受一個(gè)可選的raise_exception標(biāo)志一疯,如果存在驗(yàn)證錯(cuò)誤,它將引發(fā)一個(gè)serializers.ValidationError異常夺姑。

這些異常由REST框架提供的默認(rèn)異常處理程序自動(dòng)處理墩邀,默認(rèn)情況下將返回HTTP 400錯(cuò)誤請(qǐng)求響應(yīng)。

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

Field-level validation

.is_valid()方法接受一個(gè)可選的raise_exception標(biāo)志盏浙,如果驗(yàn)證錯(cuò)誤眉睹,它將引發(fā)一個(gè)serializers.ValidationError異常。

這些異常由REST框架提供的默認(rèn)異常處理程序自動(dòng)處理废膘,默認(rèn)情況下將返回HTTP 400錯(cuò)誤請(qǐng)求響應(yīng)竹海。

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

Field-level validation

您可以通過將.validate_ <field_name>方法添加到Serializer子類來(lái)指定自定義字段級(jí)驗(yàn)證。 這些類似于Django表單上的.clean_ <field_name>方法丐黄。

這些方法采用單個(gè)參數(shù)斋配,即需要驗(yàn)證的字段值。

您的validate_ <field_name>方法應(yīng)返回驗(yàn)證的值或提出serializers.ValidationError灌闺。 例如:

from rest_framework import serializers

class BlogPostSerializer(serializers.Serializer):
    title = serializers.CharField(max_length=100)
    content = serializers.CharField()

    def validate_title(self, value):
        """
        Check that the blog post is about Django.
        """
        if 'django' not in value.lower():
            raise serializers.ValidationError("Blog post is not about Django")
        return value

注意:如果您的<field_name>在您的串行器上聲明艰争,參數(shù)required = False,則不會(huì)在不包含該字段的情況下執(zhí)行此驗(yàn)證步驟桂对。

Object-level validation

要執(zhí)行需要訪問多個(gè)字段的任何其他驗(yàn)證甩卓,請(qǐng)將一個(gè)名為.validate()的方法添加到Serializer子類。 該方法采用單個(gè)參數(shù)蕉斜,它是字段值的字典猛频。 如果需要,它應(yīng)該引發(fā)ValidationError蛛勉,或者只返回驗(yàn)證的值鹿寻。 例如:

from rest_framework import serializers

class EventSerializer(serializers.Serializer):
    description = serializers.CharField(max_length=100)
    start = serializers.DateTimeField()
    finish = serializers.DateTimeField()

    def validate(self, data):
        """
        Check that the start is before the stop.
        """
        if data['start'] > data['finish']:
            raise serializers.ValidationError("finish must occur after start")
        return data

Validators

串行器上的各個(gè)字段可以包括驗(yàn)證器,通過在字段實(shí)例上聲明它們诽凌,例如:

def multiple_of_ten(value):
    if value % 10 != 0:
        raise serializers.ValidationError('Not a multiple of ten')

class GameRecord(serializers.Serializer):
    score = IntegerField(validators=[multiple_of_ten])
    ...

序列化器類還可以包括應(yīng)用于整套現(xiàn)場(chǎng)數(shù)據(jù)的可重用驗(yàn)證器毡熏。 這些驗(yàn)證器包含在內(nèi)部Meta類上聲明它們,如下所示:

class EventSerializer(serializers.Serializer):
    name = serializers.CharField()
    room_number = serializers.IntegerField(choices=[101, 102, 103, 201])
    date = serializers.DateField()

    class Meta:
        # Each room only has one event per day.
        validators = UniqueTogetherValidator(
            queryset=Event.objects.all(),
            fields=['room_number', 'date']
        )

有關(guān)更多信息侣诵,請(qǐng)參閱驗(yàn)證器文檔痢法。

Accessing the initial data and instance

將初始對(duì)象或查詢集傳遞給序列化器實(shí)例時(shí),該對(duì)象將以.instance形式提供杜顺。 如果沒有初始對(duì)象被傳遞财搁,那么.instance屬性將為None。

將數(shù)據(jù)傳遞給串行器實(shí)例時(shí)躬络,未修改的數(shù)據(jù)將作為.initial_data提供尖奔。 如果data關(guān)鍵字參數(shù)未被傳遞,則.initial_data屬性將不存在。

Partial updates

默認(rèn)情況下提茁,序列化程序必須傳遞所有必填字段的值淹禾,否則會(huì)引發(fā)驗(yàn)證錯(cuò)誤。 您可以使用部分參數(shù)以允許部分更新茴扁。

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

Dealing with nested objects

以前的示例適用于處理只有簡(jiǎn)單數(shù)據(jù)類型的對(duì)象铃岔,但有時(shí)我們還需要能夠表示更復(fù)雜的對(duì)象,其中對(duì)象的某些屬性可能不是簡(jiǎn)單的數(shù)據(jù)類型峭火,如字符串毁习,日期或整數(shù)。

Serializer類本身就是一個(gè)Field類型卖丸,可以用來(lái)表示一個(gè)對(duì)象類型嵌套在另一個(gè)對(duì)象之間的關(guān)系纺且。

class UserSerializer(serializers.Serializer):
    email = serializers.EmailField()
    username = serializers.CharField(max_length=100)

class CommentSerializer(serializers.Serializer):
    user = UserSerializer()
    content = serializers.CharField(max_length=200)
    created = serializers.DateTimeField()

如果嵌套表示可以選擇接受無(wú)值,則應(yīng)將required = False標(biāo)志傳遞給嵌套的序列化程序坯苹。

class CommentSerializer(serializers.Serializer):
    user = UserSerializer(required=False)  # May be an anonymous user.
    content = serializers.CharField(max_length=200)
    created = serializers.DateTimeField()

類似地,如果嵌套表示應(yīng)該是項(xiàng)目列表摇天,則應(yīng)該將許多= True標(biāo)志傳遞給嵌套的序列化粹湃。

class CommentSerializer(serializers.Serializer):
    user = UserSerializer(required=False)
    edits = EditItemSerializer(many=True)  # A nested list of 'edit' items.
    content = serializers.CharField(max_length=200)
    created = serializers.DateTimeField()

Writable nested representations

當(dāng)處理支持反序列化數(shù)據(jù)的嵌套表示時(shí),嵌套對(duì)象的任何錯(cuò)誤都嵌套在嵌套對(duì)象的字段名下泉坐。

serializer = CommentSerializer(data={'user': {'email': 'foobar', 'username': 'doe'}, 'content': 'baz'})
serializer.is_valid()
# False
serializer.errors
# {'user': {'email': [u'Enter a valid e-mail address.']}, 'created': [u'This field is required.']}

類似地为鳄,.validated_data屬性將包括嵌套的數(shù)據(jù)結(jié)構(gòu)。

Writing .create() methods for nested representations

如果您支持可寫嵌套表示腕让,則需要編寫處理保存多個(gè)對(duì)象的.create()或.update()方法孤钦。

以下示例演示如何處理創(chuàng)建具有嵌套概要文件對(duì)象的用戶。

class UserSerializer(serializers.ModelSerializer):
    profile = ProfileSerializer()

    class Meta:
        model = User
        fields = ('username', 'email', 'profile')

    def create(self, validated_data):
        profile_data = validated_data.pop('profile')
        user = User.objects.create(**validated_data)
        Profile.objects.create(user=user, **profile_data)
        return user
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末纯丸,一起剝皮案震驚了整個(gè)濱河市偏形,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌觉鼻,老刑警劉巖俊扭,帶你破解...
    沈念sama閱讀 212,816評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異坠陈,居然都是意外死亡萨惑,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門仇矾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)庸蔼,“玉大人,你說我怎么就攤上這事贮匕〗憬觯” “怎么了?”我有些...
    開封第一講書人閱讀 158,300評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)萍嬉。 經(jīng)常有香客問我乌昔,道長(zhǎng),這世上最難降的妖魔是什么壤追? 我笑而不...
    開封第一講書人閱讀 56,780評(píng)論 1 285
  • 正文 為了忘掉前任磕道,我火速辦了婚禮,結(jié)果婚禮上行冰,老公的妹妹穿的比我還像新娘溺蕉。我一直安慰自己,他們只是感情好悼做,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,890評(píng)論 6 385
  • 文/花漫 我一把揭開白布疯特。 她就那樣靜靜地躺著,像睡著了一般肛走。 火紅的嫁衣襯著肌膚如雪漓雅。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,084評(píng)論 1 291
  • 那天朽色,我揣著相機(jī)與錄音邻吞,去河邊找鬼。 笑死葫男,一個(gè)胖子當(dāng)著我的面吹牛抱冷,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播梢褐,決...
    沈念sama閱讀 39,151評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼旺遮,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了盈咳?” 一聲冷哼從身側(cè)響起耿眉,我...
    開封第一講書人閱讀 37,912評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎鱼响,沒想到半個(gè)月后跷敬,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,355評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡热押,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,666評(píng)論 2 327
  • 正文 我和宋清朗相戀三年西傀,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片桶癣。...
    茶點(diǎn)故事閱讀 38,809評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡拥褂,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出牙寞,到底是詐尸還是另有隱情饺鹃,我是刑警寧澤莫秆,帶...
    沈念sama閱讀 34,504評(píng)論 4 334
  • 正文 年R本政府宣布,位于F島的核電站悔详,受9級(jí)特大地震影響镊屎,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜茄螃,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,150評(píng)論 3 317
  • 文/蒙蒙 一缝驳、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧归苍,春花似錦用狱、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至吻氧,卻和暖如春溺忧,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背盯孙。 一陣腳步聲響...
    開封第一講書人閱讀 32,121評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工鲁森, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人镀梭。 一個(gè)月前我還...
    沈念sama閱讀 46,628評(píng)論 2 362
  • 正文 我出身青樓刀森,卻偏偏與公主長(zhǎng)得像踱启,于是被迫代替她去往敵國(guó)和親报账。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,724評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容

  • serializer只需要定義好字段即可(序列化器的字段名稱和對(duì)應(yīng)的對(duì)象的字段想對(duì)應(yīng))埠偿,任何方法都是非必須的透罢。 序...
    xncode閱讀 557評(píng)論 0 1
  • Django: csrf防御機(jī)制 csrf攻擊過程 1.用戶C打開瀏覽器,訪問受信任網(wǎng)站A冠蒋,輸入用戶名和密碼請(qǐng)求登...
    lijun_m閱讀 1,055評(píng)論 0 0
  • //我所經(jīng)歷的大數(shù)據(jù)平臺(tái)發(fā)展史(三):互聯(lián)網(wǎng)時(shí)代 ? 上篇http://www.infoq.com/cn/arti...
    葡萄喃喃囈語(yǔ)閱讀 51,199評(píng)論 10 200
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理羽圃,服務(wù)發(fā)現(xiàn),斷路器抖剿,智...
    卡卡羅2017閱讀 134,638評(píng)論 18 139
  • HTML表單 在HTML中朽寞,表單是 ... 之間元素的集合,它們?cè)试S訪問者輸入文本斩郎、選擇選項(xiàng)脑融、操作對(duì)象等等,然后將...
    蘭山小亭閱讀 3,413評(píng)論 2 14