99re热这里只有精品视频,7777色鬼xxxx欧美色妇,国产成人精品一区二三区在线观看,内射爽无广熟女亚洲,精品人妻av一区二区三区

Django drf 序列化器

2021-03-26 11:52 更新

序列化器

擴(kuò)展serializers的有用性是我們想要解決的問(wèn)題。但是,這不是一個(gè)微不足道的問(wèn)題,而是需要一些嚴(yán)肅的設(shè)計(jì)工作?!?Russell Keith-Magee, Django用戶(hù)組

序列化器允許把像查詢(xún)集和模型實(shí)例這樣的復(fù)雜數(shù)據(jù)轉(zhuǎn)換為可以輕松渲染成JSON,XML或其他內(nèi)容類(lèi)型的原生Python類(lèi)型。序列化器還提供反序列化,在驗(yàn)證傳入的數(shù)據(jù)之后允許解析數(shù)據(jù)轉(zhuǎn)換回復(fù)雜類(lèi)型。

REST framework中的serializers與Django的Form和ModelForm類(lèi)非常像。我們提供了一個(gè)Serializer類(lèi),它為你提供了強(qiáng)大的通用方法來(lái)控制響應(yīng)的輸出,以及一個(gè)ModelSerializer類(lèi),它為創(chuàng)建用于處理模型實(shí)例和查詢(xún)集的序列化程序提供了有用的快捷實(shí)現(xiàn)方式。

聲明序列化器

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

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ì)象相應(yīng)的數(shù)據(jù)。

聲明一個(gè)序列化器看起來(lái)非常像聲明一個(gè)form:

from rest_framework import serializers

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

序列化對(duì)象

我們現(xiàn)在可以用CommentSerializer去序列化一個(gè)comment或comment列表。同樣,使用Serializer類(lèi)看起來(lái)很像使用Form類(lèi)。

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ù)類(lèi)型。為了完成序列化過(guò)程,我們將數(shù)據(jù)轉(zhuǎ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"}'

反序列化對(duì)象

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

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

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

...然后我們將這些原生數(shù)據(jù)類(lèi)型恢復(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)}

保存實(shí)例

如果我們希望能夠返回基于驗(yàn)證數(shù)據(jù)的完整對(duì)象實(shí)例,我們需要實(shí)現(xiàn)其中一個(gè)或全部實(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是一個(gè)Django模型的話(huà),具體的方法可能如下所示:

    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)在當(dāng)我們反序列化數(shù)據(jù)的時(shí)候,基于驗(yàn)證過(guò)的數(shù)據(jù)我們可以調(diào)用.save()方法返回一個(gè)對(duì)象實(shí)例。

comment = serializer.save()

調(diào)用.save()方法將創(chuàng)建新實(shí)例或者更新現(xiàn)有實(shí)例,具體取決于實(shí)例化序列化器類(lèi)的時(shí)候是否傳遞了現(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ù)你序列化器類(lèi)的用例不實(shí)現(xiàn)、實(shí)現(xiàn)它們之一或都實(shí)現(xiàn)。

傳遞附加屬性到.save()

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

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

serializer.save(owner=request.user)

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

直接重寫(xiě).save()

在某些情況下.create()和.update()方法可能無(wú)意義。例如在contact form中,我們可能不會(huì)創(chuàng)建新的實(shí)例,而是發(fā)送電子郵件或其他消息。

在這些情況下,你可以選擇直接重寫(xiě).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)在不得不直接訪(fǎng)問(wèn)serializer的.validated_data屬性。

驗(yàn)證

反序列化數(shù)據(jù)的時(shí)候,你始終需要先調(diào)用is_valid()方法,然后再?lài)L試去訪(fǎng)問(wèn)經(jīng)過(guò)驗(yàn)證的數(shù)據(jù)或保存對(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è)鍵都是字段名稱(chēng),值是與該字段對(duì)應(yīng)的任何錯(cuò)誤消息的字符串列表。non_field_errors鍵可能存在,它將列出任何一般驗(yàn)證錯(cuò)誤信息。non_field_errors的名稱(chēng)可以通過(guò)REST framework設(shè)置中的NON_FIELD_ERRORS_KEY來(lái)自定義。 當(dāng)對(duì)對(duì)象列表進(jìn)行序列化時(shí),返回的錯(cuò)誤是每個(gè)反序列化項(xiàng)的字典列表。

拋出無(wú)效數(shù)據(jù)的異常

.is_valid()方法使用可選的raise_exception標(biāo)志,如果存在驗(yàn)證錯(cuò)誤將會(huì)拋出一個(gè)serializers.ValidationError異常。

這些異常由REST framework提供的默認(rèn)異常處理程序自動(dòng)處理,默認(rèn)情況下將返回HTTP 400 Bad Request響應(yīng)。

# 如果數(shù)據(jù)無(wú)效就返回400響應(yīng)
serializer.is_valid(raise_exception=True)

字段級(jí)別的驗(yàn)證

你可以通過(guò)向你的Serializer子類(lèi)中添加.validate_<field_name>方法來(lái)指定自定義字段級(jí)別的驗(yàn)證。這些類(lèi)似于Django表單中的.clean_<field_name>方法。

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

你的validate_<field_name>方法應(yīng)該返回一個(gè)驗(yàn)證過(guò)的數(shù)據(jù)或者拋出一個(gè)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參數(shù),字段不被包含的時(shí)候這個(gè)驗(yàn)證步驟就不會(huì)執(zhí)行。

對(duì)象級(jí)別的驗(yàn)證

要執(zhí)行需要訪(fǎng)問(wèn)多個(gè)字段的任何其他驗(yàn)證,請(qǐng)?zhí)砑右粋€(gè).validate()方法到你的Serializer子類(lèi)中。這個(gè)方法采用字段值字典的單個(gè)參數(shù),如果需要應(yīng)該拋出一個(gè) ValidationError異常,或者只是返回經(jīng)過(guò)驗(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

驗(yàn)證器

序列化器上的各個(gè)字段都可以包含驗(yàn)證器,通過(guò)在字段實(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])
    ...

序列化器類(lèi)還可以包括應(yīng)用于一組字段數(shù)據(jù)的可重用的驗(yàn)證器。這些驗(yàn)證器要在內(nèi)部的Meta類(lèi)中聲明,如下所示:

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

    class Meta:
        # 每間屋子每天只能有1個(gè)活動(dòng)。
        validators = UniqueTogetherValidator(
            queryset=ToDoItem.objects.all(),
            fields=['room_number', 'date']
        )

更多信息請(qǐng)參閱 validators文檔。

訪(fǎng)問(wèn)初始數(shù)據(jù)和實(shí)例

將初始化對(duì)象或者查詢(xún)集傳遞給序列化實(shí)例時(shí),可以通過(guò).instance訪(fǎng)問(wèn)。如果沒(méi)有傳遞初始化對(duì)象,那么.instance屬性將是None。

將數(shù)據(jù)傳遞給序列化器實(shí)例時(shí),未修改的數(shù)據(jù)可以通過(guò).initial_data獲取。如果沒(méi)有傳遞data關(guān)鍵字參數(shù),那么.initial_data屬性就不存在。

部分更新

默認(rèn)情況下,序列化器必須傳遞所有必填字段的值,否則就會(huì)引發(fā)驗(yàn)證錯(cuò)誤。你可以使用 partial參數(shù)來(lái)允許部分更新。

# 使用部分?jǐn)?shù)據(jù)更新`comment` 
serializer = CommentSerializer(comment, data={'content': u'foo bar'}, partial=True)

處理嵌套對(duì)象

前面的實(shí)例適用于處理只有簡(jiǎn)單數(shù)據(jù)類(lèi)型的對(duì)象,但是有時(shí)候我們也需要表示更復(fù)雜的對(duì)象,其中對(duì)象的某些屬性可能不是字符串、日期、整數(shù)這樣簡(jiǎn)單的數(shù)據(jù)類(lèi)型。

Serializer類(lèi)本身也是一種Field,并且可以用來(lái)表示一個(gè)對(duì)象類(lèi)型嵌套在另一個(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()

如果嵌套表示可以接收 None值,則應(yīng)該將 required=False標(biāo)志傳遞給嵌套的序列化器。

class CommentSerializer(serializers.Serializer):
    user = UserSerializer(required=False)  # 可能是匿名用戶(hù)。
    content = serializers.CharField(max_length=200)
    created = serializers.DateTimeField()

類(lèi)似的,如果嵌套的關(guān)聯(lián)字段可以接收一個(gè)列表,那么應(yīng)該將many=True標(biāo)志傳遞給嵌套的序列化器。

class CommentSerializer(serializers.Serializer):
    user = UserSerializer(required=False)
    edits = EditItemSerializer(many=True)  # edit'項(xiàng)的嵌套列表
    content = serializers.CharField(max_length=200)
    created = serializers.DateTimeField()

可寫(xiě)的嵌套表示

當(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.']}

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

為嵌套關(guān)系定義.create()方法

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

下面的示例演示如何處理創(chuàng)建一個(gè)具有嵌套的概要信息對(duì)象的用戶(hù)。

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

為嵌套關(guān)系定義.update()方法

對(duì)于更新,你需要仔細(xì)考慮如何處理關(guān)聯(lián)字段的更新。 例如,如果關(guān)聯(lián)字段的值是None,或者沒(méi)有提供,那么會(huì)發(fā)生下面哪一項(xiàng)?

  • 在數(shù)據(jù)庫(kù)中將關(guān)聯(lián)字段設(shè)置成NULL。
  • 刪除關(guān)聯(lián)的實(shí)例。
  • 忽略數(shù)據(jù)并保留這個(gè)實(shí)例。
  • 拋出驗(yàn)證錯(cuò)誤。

下面是我們之前UserSerializer類(lèi)中update()方法的一個(gè)例子。

    def update(self, instance, validated_data):
        profile_data = validated_data.pop('profile')
        # 除非應(yīng)用程序正確執(zhí)行,
        # 保證這個(gè)字段一直被設(shè)置,
        # 否則就應(yīng)該拋出一個(gè)需要處理的`DoesNotExist`。
        profile = instance.profile

        instance.username = validated_data.get('username', instance.username)
        instance.email = validated_data.get('email', instance.email)
        instance.save()

        profile.is_premium_member = profile_data.get(
            'is_premium_member',
            profile.is_premium_member
        )
        profile.has_support_contract = profile_data.get(
            'has_support_contract',
            profile.has_support_contract
         )
        profile.save()

        return instance

因?yàn)榍短钻P(guān)系的創(chuàng)建和更新行為可能不明確,并且可能需要關(guān)聯(lián)模型間的復(fù)雜依賴(lài)關(guān)系,REST framework 3 要求你始終明確的定義這些方法。默認(rèn)的ModelSerializer .create()和.update()方法不包括對(duì)可寫(xiě)嵌套關(guān)聯(lián)的支持。

提供自動(dòng)支持某種類(lèi)型的自動(dòng)寫(xiě)入嵌套關(guān)聯(lián)的第三方包可能與3.1版本一同放出。

處理在模型管理類(lèi)中保存關(guān)聯(lián)實(shí)例

在序列化器中保存多個(gè)相關(guān)實(shí)例的另一種方法是編寫(xiě)處理創(chuàng)建正確實(shí)例的自定義模型管理器類(lèi)。

例如,假設(shè)我們想確保User實(shí)例和Profile實(shí)例總是作為一對(duì)一起創(chuàng)建。我們可能會(huì)寫(xiě)一個(gè)類(lèi)似這樣的自定義管理器類(lèi):

class UserManager(models.Manager):
    ...

    def create(self, username, email, is_premium_member=False, has_support_contract=False):
        user = User(username=username, email=email)
        user.save()
        profile = Profile(
            user=user,
            is_premium_member=is_premium_member,
            has_support_contract=has_support_contract
        )
        profile.save()
        return user

這個(gè)管理器類(lèi)現(xiàn)在更好的封裝了用戶(hù)實(shí)例和用戶(hù)信息實(shí)例總是在同一時(shí)間創(chuàng)建。我們?cè)谛蛄谢黝?lèi)上的.create()方法現(xiàn)在能夠用新的管理器方法重寫(xiě)。

def create(self, validated_data):
    return User.objects.create(
        username=validated_data['username'],
        email=validated_data['email']
        is_premium_member=validated_data['profile']['is_premium_member']
        has_support_contract=validated_data['profile']['has_support_contract']
    )

有關(guān)此方法的更多詳細(xì)信息,請(qǐng)參閱Django文檔中的 模型管理器使用模型和管理器類(lèi)的相關(guān)博客

處理多個(gè)對(duì)象

Serializer類(lèi)還可以序列化或反序列化對(duì)象的列表。

序列化多個(gè)對(duì)象

為了能夠序列化一個(gè)查詢(xún)集或者一個(gè)對(duì)象列表而不是一個(gè)單獨(dú)的對(duì)象,應(yīng)該在實(shí)例化序列化器類(lèi)的時(shí)候傳一個(gè)many=True參數(shù)。這樣就能序列化一個(gè)查詢(xún)集或一個(gè)對(duì)象列表。

queryset = Book.objects.all()
serializer = BookSerializer(queryset, many=True)
serializer.data
# [
#     {'id': 0, 'title': 'The electric kool-aid acid test', 'author': 'Tom Wolfe'},
#     {'id': 1, 'title': 'If this is a man', 'author': 'Primo Levi'},
#     {'id': 2, 'title': 'The wind-up bird chronicle', 'author': 'Haruki Murakami'}
# ]

反序列化多個(gè)對(duì)象

反序列化多個(gè)對(duì)象默認(rèn)支持多個(gè)對(duì)象的創(chuàng)建,但是不支持多個(gè)對(duì)象的更新。有關(guān)如何支持或自定義這些情況的更多信息,請(qǐng)查閱這個(gè)文檔ListSerializer。

包括額外的上下文

在某些情況下,除了要序列化的對(duì)象之外,還需要為序列化程序提供額外的上下文。一個(gè)常見(jiàn)的情況是,如果你使用包含超鏈接關(guān)系的序列化程序,這需要序列化器能夠訪(fǎng)問(wèn)當(dāng)前的請(qǐng)求以便正確生成完全限定的URL。

你可以在實(shí)例化序列化器的時(shí)候傳遞一個(gè)context參數(shù)來(lái)傳遞任意的附加上下文。例如:

serializer = AccountSerializer(account, context={'request': request})
serializer.data
# {'id': 6, 'owner': u'denvercoder9', 'created': datetime.datetime(2013, 2, 12, 09, 44, 56, 678870), 'details': 'http://example.com/accounts/6/details'}

這個(gè)上下文的字典可以在任何序列化器字段的邏輯中使用,例如.to_representation()方法中可以通過(guò)訪(fǎng)問(wèn)self.context屬性獲取上下文字典。

ModelSerializer

通常你會(huì)想要與Django模型相對(duì)應(yīng)的序列化類(lèi)。

ModelSerializer類(lèi)能夠讓你自動(dòng)創(chuàng)建一個(gè)具有模型中相應(yīng)字段的Serializer類(lèi)。

這個(gè)ModelSerializer類(lèi)和常規(guī)的Serializer類(lèi)一樣,不同的是:

  • 它根據(jù)模型自動(dòng)生成一組字段。
  • 它自動(dòng)生成序列化器的驗(yàn)證器,比如unique_together驗(yàn)證器。
  • 它默認(rèn)簡(jiǎn)單實(shí)現(xiàn)了.create()方法和.update()方法。

聲明一個(gè)ModelSerializer如下:

class AccountSerializer(serializers.ModelSerializer):
    class Meta:
        model = Account
        fields = ('id', 'account_name', 'users', 'created')

默認(rèn)情況下,所有的模型的字段都將映射到序列化器上相應(yīng)的字段。

模型中任何關(guān)聯(lián)字段比如外鍵都將映射到PrimaryKeyRelatedField字段。默認(rèn)情況下不包括反向關(guān)聯(lián),除非像serializer relations文檔中規(guī)定的那樣顯式包含。

檢查ModelSerializer

序列化類(lèi)生成有用的詳細(xì)表示字符串,允許你全面檢查其字段的狀態(tài)。 這在使用ModelSerializers時(shí)特別有用,因?yàn)槟阆氪_定自動(dòng)創(chuàng)建了哪些字段和驗(yàn)證器。

要檢查的話(huà),打開(kāi)Django shell,執(zhí)行 python manage.py shell,然后導(dǎo)入序列化器類(lèi),實(shí)例化它,并打印對(duì)象的表示:

>>> from myapp.serializers import AccountSerializer
>>> serializer = AccountSerializer()
>>> print(repr(serializer))
AccountSerializer():
    id = IntegerField(label='ID', read_only=True)
    name = CharField(allow_blank=True, max_length=100, required=False)
    owner = PrimaryKeyRelatedField(queryset=User.objects.all())

指定要包括的字段

如果你希望在模型序列化器中使用默認(rèn)字段的一部分,你可以使用fields或exclude選項(xiàng)來(lái)執(zhí)行此操作,就像使用ModelForm一樣。強(qiáng)烈建議你使用fields屬性顯式的設(shè)置要序列化的字段。這樣就不太可能因?yàn)槟阈薷牧四P投鵁o(wú)意中暴露了數(shù)據(jù)。

例如:

class AccountSerializer(serializers.ModelSerializer):
    class Meta:
        model = Account
        fields = ('id', 'account_name', 'users', 'created')

你還可以將fields屬性設(shè)置成'__all__'來(lái)表明使用模型中的所有字段。

例如:

class AccountSerializer(serializers.ModelSerializer):
    class Meta:
        model = Account
        fields = '__all__'

你可以將exclude屬性設(shè)置成一個(gè)從序列化器中排除的字段列表。

例如:

class AccountSerializer(serializers.ModelSerializer):
    class Meta:
        model = Account
        exclude = ('users',)

在上面的例子中,如果Account模型有三個(gè)字段account_name,users和created,那么只有 account_name和created會(huì)被序列化。

在fields和exclude屬性中的名稱(chēng),通常會(huì)映射到模型類(lèi)中的模型字段。

或者fields選項(xiàng)中的名稱(chēng)可以映射到模型類(lèi)中不存在任何參數(shù)的屬性或方法。

指定嵌套序列化

默認(rèn)ModelSerializer使用主鍵進(jìn)行關(guān)聯(lián),但是你也可以使用depth選項(xiàng)輕松生成嵌套關(guān)聯(lián):

class AccountSerializer(serializers.ModelSerializer):
    class Meta:
        model = Account
        fields = ('id', 'account_name', 'users', 'created')
        depth = 1

depth選項(xiàng)應(yīng)該設(shè)置一個(gè)整數(shù)值,表明應(yīng)該遍歷的關(guān)聯(lián)深度。

如果要自定義序列化的方式你需要自定義該子段。

明確指定字段

你可以通過(guò)在ModelSerializer類(lèi)上聲明字段來(lái)增加額外的字段或者重寫(xiě)默認(rèn)的字段,就和在Serializer類(lèi)一樣的。

class AccountSerializer(serializers.ModelSerializer):
    url = serializers.CharField(source='get_absolute_url', read_only=True)
    groups = serializers.PrimaryKeyRelatedField(many=True)

    class Meta:
        model = Account

額外的字段可以對(duì)應(yīng)模型上任何屬性或可調(diào)用的方法。

指定只讀字段

你可能希望將多個(gè)字段指定為只讀,而不是顯式的為每個(gè)字段添加read_only=True屬性,這種情況你可以使用Meta的read_only_fields選項(xiàng)。

該選項(xiàng)應(yīng)該是字段名稱(chēng)的列表或元祖,并像下面這樣聲明:

class AccountSerializer(serializers.ModelSerializer):
    class Meta:
        model = Account
        fields = ('id', 'account_name', 'users', 'created')
        read_only_fields = ('account_name',)

模型中已經(jīng)設(shè)置editable=False的字段和默認(rèn)就被設(shè)置為只讀的AutoField字段都不需要添加到read_only_fields選項(xiàng)中。

注意: 有一種特殊情況,其中一個(gè)只讀字段是模型級(jí)別unique_together約束的一部分。在這種情況下,序列化器類(lèi)需要該字段才能驗(yàn)證約束,但也不能由用戶(hù)編輯。

處理此問(wèn)題的正確方法是在序列化器上顯式指定該字段,同時(shí)提供read_only=True和default=…關(guān)鍵字參數(shù)。

這種情況的一個(gè)例子就是對(duì)于一個(gè)和其他標(biāo)識(shí)符unique_together的當(dāng)前認(rèn)證的User是只讀的。 在這種情況下你可以像下面這樣聲明user字段:

user = serializers.PrimaryKeyRelatedField(read_only=True, default=serializers.CurrentUserDefault())

有關(guān)UniqueTogetherValidatorCurrentUserDefault類(lèi)的詳細(xì)文檔,請(qǐng)查閱驗(yàn)證器的文檔。

附加關(guān)鍵字參數(shù)

還可以通過(guò)使用extra_kwargs選項(xiàng)快捷地在字段上指定任意附加的關(guān)鍵字參數(shù)。在read_only_fields這種情況下,你不需要在序列化器上式的聲明該字段。

這個(gè)選項(xiàng)是一個(gè)將具體字段名稱(chēng)當(dāng)作鍵值的字典。例如:

class CreateUserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ('email', 'username', 'password')
        extra_kwargs = {'password': {'write_only': True}}

    def create(self, validated_data):
        user = User(
            email=validated_data['email'],
            username=validated_data['username']
        )
        user.set_password(validated_data['password'])
        user.save()
        return user

關(guān)聯(lián)字段

在序列化模型實(shí)例的時(shí)候,你可以選擇多種不同的方式來(lái)表示關(guān)聯(lián)關(guān)系。對(duì)于ModelSerializer默認(rèn)是使用相關(guān)實(shí)例的主鍵。

替代的其他方法包括使用超鏈接序列化,序列化完整的嵌套表示或者使用自定義表示的序列化。

更多詳細(xì)信息請(qǐng)查閱serializer relations文檔。

自定義字段映射

ModelSerializer類(lèi)還公開(kāi)了一個(gè)可以覆蓋的API,以便在實(shí)例化序列化器時(shí)改變序列化器字段的自動(dòng)確定。

通常情況下,如果ModelSerializer沒(méi)有生成默認(rèn)情況下你需要的字段,那么你應(yīng)該將它們顯式地添加到類(lèi)中,或者直接使用常規(guī)的Serializer類(lèi)。但是在某些情況下,你可能需要?jiǎng)?chuàng)建一個(gè)新的基類(lèi),來(lái)定義給任意模型創(chuàng)建序列化字段的方式。

.serializer_field_mapping

將Django model類(lèi)映射到REST framework serializer類(lèi)。你可以覆寫(xiě)這個(gè)映射來(lái)更改每個(gè)模型應(yīng)該使用的默認(rèn)序列化器類(lèi)。

.serializer_related_field

這個(gè)屬性應(yīng)是序列化器字段類(lèi),默認(rèn)情況下用于關(guān)聯(lián)字段。

對(duì)于ModelSerializer此屬性默認(rèn)是PrimaryKeyRelatedField。

對(duì)于HyperlinkedModelSerializer此屬性默認(rèn)是serializers.HyperlinkedRelatedField。

serializer_url_field

應(yīng)該用于序列化器上任何url字段的序列化器字段類(lèi)。

默認(rèn)是 serializers.HyperlinkedIdentityField

serializer_choice_field

應(yīng)用于序列化器上任何選擇字段的序列化器字段類(lèi)。

默認(rèn)是serializers.ChoiceField

The field_class和field_kwargs API

調(diào)用下面的方法來(lái)確定應(yīng)該自動(dòng)包含在序列化器類(lèi)中每個(gè)字段的類(lèi)和關(guān)鍵字參數(shù)。這些方法都應(yīng)該返回兩個(gè) (field_class, field_kwargs)元祖。

.build_standard_field(self, field_name, model_field)

調(diào)用后生成對(duì)應(yīng)標(biāo)準(zhǔn)模型字段的序列化器字段。

默認(rèn)實(shí)現(xiàn)是根據(jù)serializer_field_mapping屬性返回一個(gè)序列化器類(lèi)。

.build_relational_field(self, field_name, relation_info)

調(diào)用后生成對(duì)應(yīng)關(guān)聯(lián)模型字段的序列化器字段。

默認(rèn)實(shí)現(xiàn)是根據(jù)serializer_relational_field屬性返回一個(gè)序列化器類(lèi)。

這里的relation_info參數(shù)是一個(gè)命名元祖,包含model_field,related_model,to_many和has_through_model屬性。

.build_nested_field(self, field_name, relation_info, nested_depth)

當(dāng)depth選項(xiàng)被設(shè)置時(shí),被調(diào)用后生成一個(gè)對(duì)應(yīng)到關(guān)聯(lián)模型字段的序列化器字段。

默認(rèn)實(shí)現(xiàn)是動(dòng)態(tài)的創(chuàng)建一個(gè)基于ModelSerializer或HyperlinkedModelSerializer的嵌套的序列化器類(lèi)。

nested_depth的值是depth的值減1。

relation_info參數(shù)是一個(gè)命名元祖,包含 model_field,related_model,to_many和has_through_model屬性。

.build_property_field(self, field_name, model_class)

被調(diào)用后生成一個(gè)對(duì)應(yīng)到模型類(lèi)中屬性或無(wú)參數(shù)方法的序列化器字段。

默認(rèn)實(shí)現(xiàn)是返回一個(gè)ReadOnlyField類(lèi)。

.build_url_field(self, field_name, model_class)

被調(diào)用后為序列化器自己的url字段生成一個(gè)序列化器字段。默認(rèn)實(shí)現(xiàn)是返回一個(gè)HyperlinkedIdentityField類(lèi)。

.build_unknown_field(self, field_name, model_class)

當(dāng)字段名稱(chēng)沒(méi)有對(duì)應(yīng)到任何模型字段或者模型屬性時(shí)調(diào)用。 默認(rèn)實(shí)現(xiàn)會(huì)拋出一個(gè)錯(cuò)誤,盡管子類(lèi)可能會(huì)自定義該行為。

HyperlinkedModelSerializer

HyperlinkedModelSerializer類(lèi)類(lèi)似于ModelSerializer類(lèi),不同之處在于它使用超鏈接來(lái)表示關(guān)聯(lián)關(guān)系而不是主鍵。

默認(rèn)情況下序列化器將包含一個(gè)url字段而不是主鍵字段。

url字段將使用HyperlinkedIdentityField字段來(lái)表示,模型的任何關(guān)聯(lián)都將使用HyperlinkedRelatedField字段來(lái)表示。

你可以通過(guò)將主鍵添加到fields選項(xiàng)中來(lái)顯式的包含,例如:

class AccountSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Account
        fields = ('url', 'id', 'account_name', 'users', 'created')

絕對(duì)和相對(duì)URL

當(dāng)實(shí)例化一個(gè)HyperlinkedModelSerializer時(shí),你必須在序列化器的上下文中包含當(dāng)前的request值,例如:

serializer = AccountSerializer(queryset, context={'request': request})

這樣做將確保超鏈接可以包含恰當(dāng)?shù)闹鳈C(jī)名,一邊生成完全限定的URL,例如:

http://api.example.com/accounts/1/

而不是相對(duì)的URL,例如:

/accounts/1/

如果你真的要使用相對(duì)URL,你應(yīng)該明確的在序列化器上下文中傳遞一個(gè){'request': None}。

如何確定超鏈接視圖

需要一種確定哪些視圖能應(yīng)用超鏈接到模型實(shí)例的方法。

默認(rèn)情況下,超鏈接期望對(duì)應(yīng)到一個(gè)樣式能匹配'{model_name}-detail'的視圖,并通過(guò)pk關(guān)鍵字參數(shù)查找實(shí)例。

你可以通過(guò)在extra_kwargs中設(shè)置view_name和lookup_field中的一個(gè)或兩個(gè)來(lái)重寫(xiě)URL字段視圖名稱(chēng)和查詢(xún)字段。如下所示:

class AccountSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Account
        fields = ('account_url', 'account_name', 'users', 'created')
        extra_kwargs = {
            'url': {'view_name': 'accounts', 'lookup_field': 'account_name'},
            'users': {'lookup_field': 'username'}
        }

或者你可以顯式的設(shè)置序列化器上的字段。例如:

class AccountSerializer(serializers.HyperlinkedModelSerializer):
    url = serializers.HyperlinkedIdentityField(
        view_name='accounts',
        lookup_field='slug'
    )
    users = serializers.HyperlinkedRelatedField(
        view_name='user-detail',
        lookup_field='username',
        many=True,
        read_only=True
    )

    class Meta:
        model = Account
        fields = ('url', 'account_name', 'users', 'created')

提示:正確匹配超鏈接表示和你的URL配置有時(shí)可能會(huì)有點(diǎn)困難。打印一個(gè)HyperlinkedModelSerializer實(shí)例的repr是一個(gè)特別有用的方式來(lái)檢查關(guān)聯(lián)關(guān)系映射的那些視圖名稱(chēng)和查詢(xún)字段。

更改URL字段名稱(chēng)

URL字段的名稱(chēng)默認(rèn)為'url'。你可以通過(guò)使用URL_FIELD_NAME設(shè)置進(jìn)行全局性修改。

ListSerializer

ListSerializer類(lèi)能夠序列化和一次驗(yàn)證多個(gè)對(duì)象。你通常不需要直接使用ListSerializer,而是應(yīng)該在實(shí)例化一個(gè)序列化器時(shí)簡(jiǎn)單地傳遞一個(gè)many=True參數(shù)。

當(dāng)一個(gè)序列化器在帶有many=True選項(xiàng)被序列化時(shí),將創(chuàng)建一個(gè)ListSerializer實(shí)例。該序列化器類(lèi)將成為L(zhǎng)istSerializer類(lèi)的子類(lèi)。

下面的參數(shù)也可以傳遞給ListSerializer字段或者一個(gè)帶有many=True參數(shù)的序列化器。

allow_empty

默認(rèn)是True,但是如果你不想把空列表當(dāng)作有效輸入的話(huà)可以把它設(shè)置成False。

自定義ListSerializer行為

下面是你可能希望要定制ListSerializer行為的一些情況。例如:

  • 你希望提供列表的特定驗(yàn)證,例如檢查一個(gè)元素是否與列表中的另外一個(gè)元素沖突。
  • 你要自定義多個(gè)對(duì)象的創(chuàng)建或更新行為。

對(duì)于這些情況,當(dāng)你可以通過(guò)使用序列化器類(lèi)的Meta類(lèi)下面的list_serializer_class選項(xiàng)來(lái)修改當(dāng)many=True時(shí)正在使用的類(lèi)。

例如:

class CustomListSerializer(serializers.ListSerializer):
    ...

class CustomSerializer(serializers.Serializer):
    ...
    class Meta:
        list_serializer_class = CustomListSerializer

自定義多個(gè)對(duì)象的創(chuàng)建

多個(gè)對(duì)象的創(chuàng)建默認(rèn)實(shí)現(xiàn)是簡(jiǎn)單地調(diào)用列表中每個(gè)對(duì)象的.create()方法。如果要自定義實(shí)現(xiàn),那么你需要自定義當(dāng)被傳遞many=True參數(shù)時(shí)使用的ListSerializer類(lèi)中的.create()方法。

例如:

class BookListSerializer(serializers.ListSerializer):
    def create(self, validated_data):
        books = [Book(**item) for item in validated_data]
        return Book.objects.bulk_create(books)

class BookSerializer(serializers.Serializer):
    ...
    class Meta:
        list_serializer_class = BookListSerializer

自定義多對(duì)象的更新

默認(rèn)情況下,ListSerializer類(lèi)不支持多對(duì)象的更新。這是因?yàn)椴迦牒蛣h除的預(yù)期行為是不明確的。

要支持多對(duì)象更新的話(huà)你需要自己明確地實(shí)現(xiàn)。編寫(xiě)多個(gè)對(duì)象更新的代碼時(shí)要注意以下幾點(diǎn):

  • 如何確定數(shù)據(jù)列表中的每個(gè)元素應(yīng)該對(duì)應(yīng)更新哪個(gè)實(shí)例?
  • 如何處理插入?它們是無(wú)效的?還是創(chuàng)建新對(duì)象?
  • 移除應(yīng)該如何處理?它們是要?jiǎng)h除對(duì)象還是刪除關(guān)聯(lián)關(guān)系?它們應(yīng)該被忽略還是提示無(wú)效操作?
  • 排序如何處理?改變兩個(gè)元素的位置是否意味著任何狀態(tài)改變或者應(yīng)該被忽視?

你需要向?qū)嵗蛄谢髦酗@式添加一個(gè)id字段。默認(rèn)隱式生成的id字段是read_only。這就導(dǎo)致它在更新時(shí)被刪除。一旦你明確地聲明它,它將在列表序列化器的update方法中可用。

下面是一個(gè)你可以選擇用來(lái)做多個(gè)對(duì)象更新的示例:

class BookListSerializer(serializers.ListSerializer):
    def update(self, instance, validated_data):
        # Maps for id->instance and id->data item.
        book_mapping = {book.id: book for book in instance}
        data_mapping = {item['id']: item for item in validated_data}

        # Perform creations and updates.
        ret = []
        for book_id, data in data_mapping.items():
            book = book_mapping.get(book_id, None)
            if book is None:
                ret.append(self.child.create(data))
            else:
                ret.append(self.child.update(book, data))

        # Perform deletions.
        for book_id, book in book_mapping.items():
            if book_id not in data_mapping:
                book.delete()

        return ret

class BookSerializer(serializers.Serializer):
    # 我們需要使用主鍵來(lái)識(shí)別列表中的元素,
    # 所以在這里使用可寫(xiě)的字段,而不是默認(rèn)的只讀字段。
    id = serializers.IntegerField()

    ...
    id = serializers.IntegerField(required=False)

    class Meta:
        list_serializer_class = BookListSerializer

類(lèi)似于REST framework 2中allow_add_remove的自動(dòng)支持多個(gè)對(duì)象更新操作可能會(huì)在3.1版本的第三方包中提供。

自定義ListSerializer初始化

當(dāng)帶有many=True參數(shù)的序列化器被實(shí)例化時(shí),我們需要確定哪些參數(shù)和關(guān)鍵字參數(shù)應(yīng)該被傳遞給子類(lèi)Serializer和父類(lèi)ListSerializer的.__init__()方法。

默認(rèn)實(shí)現(xiàn)是將所有參數(shù)都傳遞給兩個(gè)類(lèi),出了validators和任何關(guān)鍵字參數(shù)。這兩個(gè)參數(shù)都假定用于子序列化器類(lèi)。

偶爾,你可能需要明確指定當(dāng)被傳遞many=True參數(shù)時(shí),子類(lèi)和父類(lèi)應(yīng)該如何實(shí)例化。你可以使用many_init類(lèi)方法來(lái)執(zhí)行此操作。

    @classmethod
    def many_init(cls, *args, **kwargs):
        # 實(shí)例化子序列化器類(lèi)。
        kwargs['child'] = cls()
        # 實(shí)例化列表序列化父類(lèi)
        return CustomListSerializer(*args, **kwargs)

BaseSerializer

BaseSerializer 可以很簡(jiǎn)單的用來(lái)替代序列化和反序列化的樣式。

該類(lèi)實(shí)現(xiàn)與Serializer類(lèi)相同的基本API:

  • .data - 返回傳出的原始數(shù)據(jù)。
  • .is_valid() - 反序列化并驗(yàn)證傳入的數(shù)據(jù)。
  • .validated_data - 返回經(jīng)過(guò)驗(yàn)證后的傳入數(shù)據(jù)。
  • .errors - 返回驗(yàn)證期間的錯(cuò)誤。
  • .save() - 將驗(yàn)證的數(shù)據(jù)保留到對(duì)象實(shí)例中。

它還有可以覆寫(xiě)的四種方法,具體取決于你想要序列化類(lèi)支持的功能:

  • .to_representation() - 重寫(xiě)此方法來(lái)改變讀取操作的序列化結(jié)果。
  • .to_internal_value() - 重寫(xiě)此方法來(lái)改變寫(xiě)入操作的序列化結(jié)果。
  • .create() 和 .update() - 重寫(xiě)其中一個(gè)或兩個(gè)來(lái)改變保存實(shí)例時(shí)的動(dòng)作。

因?yàn)榇祟?lèi)提供與Serializer類(lèi)相同的接口,所以你可以將它與現(xiàn)有的基于類(lèi)的通用視圖一起使用,就像使用常規(guī)Serializer或ModelSerializer一樣。

這樣做時(shí)你需要注意到的唯一區(qū)別是BaseSerializer類(lèi)并不會(huì)在可瀏覽的API頁(yè)面中生成HTML表單。

只讀的 BaseSerializer classes

要使用BaseSerializer類(lèi)實(shí)現(xiàn)只讀序列化程序,我們只需要覆寫(xiě).to_representation()方法。讓我們看一個(gè)簡(jiǎn)單的Django模型的示例:

class HighScore(models.Model):
    created = models.DateTimeField(auto_now_add=True)
    player_name = models.CharField(max_length=10)
    score = models.IntegerField()

創(chuàng)建一個(gè)只讀的序列化程序來(lái)將HighScore實(shí)例轉(zhuǎn)換為原始數(shù)據(jù)類(lèi)型非常簡(jiǎn)單。

class HighScoreSerializer(serializers.BaseSerializer):
    def to_representation(self, obj):
        return {
            'score': obj.score,
            'player_name': obj.player_name
        }

我們現(xiàn)在可以使用這個(gè)類(lèi)來(lái)序列化單個(gè)HighScore實(shí)例:

@api_view(['GET'])
def high_score(request, pk):
    instance = HighScore.objects.get(pk=pk)
    serializer = HighScoreSerializer(instance)
    return Response(serializer.data)

或者使用它來(lái)序列化多個(gè)實(shí)例:

@api_view(['GET'])
def all_high_scores(request):
    queryset = HighScore.objects.order_by('-score')
    serializer = HighScoreSerializer(queryset, many=True)
    return Response(serializer.data)
Read-write BaseSerializer classes

要?jiǎng)?chuàng)建一個(gè)讀寫(xiě)都支持的序列化器,我們首先需要實(shí)現(xiàn).to_internal_value()方法。這個(gè)方法返回用來(lái)構(gòu)造對(duì)象實(shí)例的經(jīng)過(guò)驗(yàn)證的值,如果提供的數(shù)據(jù)格式不正確,則可能引發(fā)ValidationError。

一旦你實(shí)現(xiàn)了.to_internal_value()方法,那些基礎(chǔ)的驗(yàn)證API都會(huì)在序列化對(duì)象上可用了,你就可以使用.is_valid(), .validated_data 和 .errors 方法。

如果你還想支持.save(),你還需要實(shí)現(xiàn).create()和.update()方法中的一個(gè)或兩個(gè)。

下面就是完整版的,支持讀、寫(xiě)操作的 HighScoreSerializer 完整示例了。

class HighScoreSerializer(serializers.BaseSerializer):
    def to_internal_value(self, data):
        score = data.get('score')
        player_name = data.get('player_name')

        # 執(zhí)行數(shù)據(jù)有效性校驗(yàn)
        if not score:
            raise ValidationError({
                'score': 'This field is required.'
            })
        if not player_name:
            raise ValidationError({
                'player_name': 'This field is required.'
            })
        if len(player_name) > 10:
            raise ValidationError({
                'player_name': 'May not be more than 10 characters.'
            })

        # 返回通過(guò)驗(yàn)證的數(shù)據(jù) 這用來(lái)作為 `.validated_data` 屬性的值。
        return {
            'score': int(score),
            'player_name': player_name
        }

    def to_representation(self, obj):
        return {
            'score': obj.score,
            'player_name': obj.player_name
        }

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

創(chuàng)建一個(gè)新的基類(lèi)

BaseSerializer類(lèi)還可以用來(lái)創(chuàng)建新的通用序列化程序基類(lèi)來(lái)處理特定的序列化樣式或者用來(lái)整合備用存儲(chǔ)后端。

下面這個(gè)類(lèi)是一個(gè)可以將任意對(duì)象強(qiáng)制轉(zhuǎn)換為基本表示的通用序列化程序的示例。

class ObjectSerializer(serializers.BaseSerializer):
    """
    一個(gè)只讀序列化程序,它將任意復(fù)雜的對(duì)象強(qiáng)制轉(zhuǎn)換為內(nèi)置數(shù)據(jù)類(lèi)型表示。
    """
    def to_representation(self, obj):
        for attribute_name in dir(obj):
            attribute = getattr(obj, attribute_name)
            if attribute_name('_'):
                # 忽略私有屬性
                pass
            elif hasattr(attribute, '__call__'):
                # 忽略方法和其他可調(diào)用對(duì)象
                pass
            elif isinstance(attribute, (str, int, bool, float, type(None))):
                # 內(nèi)置的原始數(shù)據(jù)類(lèi)型不做修改
                output[attribute_name] = attribute
            elif isinstance(attribute, list):
                # 遞歸處理列表中的對(duì)象
                output[attribute_name] = [
                    self.to_representation(item) for item in attribute
                ]
            elif isinstance(attribute, dict):
                # 遞歸處理字典中的對(duì)象
                output[attribute_name] = {
                    str(key): self.to_representation(value)
                    for key, value in attribute.items()
                }
            else:
                # 將其他數(shù)據(jù)類(lèi)型強(qiáng)制轉(zhuǎn)換為字符串表示
                output[attribute_name] = str(attribute)


以上內(nèi)容是否對(duì)您有幫助:
在線(xiàn)筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)