擴(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()
我們現(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"}'
反序列化是類(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)}
如果我們希望能夠返回基于驗(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)。
有時(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ù)中。
在某些情況下.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屬性。
反序列化數(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)的字典列表。
.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)
你可以通過(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í)行。
要執(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
序列化器上的各個(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文檔。
將初始化對(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)
前面的實(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()
當(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)。
如果你支持可寫(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
對(duì)于更新,你需要仔細(xì)考慮如何處理關(guān)聯(lián)字段的更新。 例如,如果關(guān)聯(lián)字段的值是None,或者沒(méi)有提供,那么會(huì)發(fā)生下面哪一項(xiàng)?
下面是我們之前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版本一同放出。
在序列化器中保存多個(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)博客。
Serializer類(lèi)還可以序列化或反序列化對(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ì)象默認(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屬性獲取上下文字典。
通常你會(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)一樣,不同的是:
聲明一個(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ī)定的那樣顯式包含。
序列化類(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)UniqueTogetherValidator和CurrentUserDefault類(lèi)的詳細(xì)文檔,請(qǐng)查閱驗(yàn)證器的文檔。
還可以通過(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
在序列化模型實(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)建序列化字段的方式。
將Django model類(lèi)映射到REST framework serializer類(lèi)。你可以覆寫(xiě)這個(gè)映射來(lái)更改每個(gè)模型應(yīng)該使用的默認(rèn)序列化器類(lèi)。
這個(gè)屬性應(yīng)是序列化器字段類(lèi),默認(rèn)情況下用于關(guān)聯(lián)字段。
對(duì)于ModelSerializer此屬性默認(rèn)是PrimaryKeyRelatedField。
對(duì)于HyperlinkedModelSerializer此屬性默認(rèn)是serializers.HyperlinkedRelatedField。
應(yīng)該用于序列化器上任何url字段的序列化器字段類(lèi)。
默認(rèn)是 serializers.HyperlinkedIdentityField
應(yīng)用于序列化器上任何選擇字段的序列化器字段類(lèi)。
默認(rèn)是serializers.ChoiceField
調(diào)用下面的方法來(lái)確定應(yīng)該自動(dòng)包含在序列化器類(lèi)中每個(gè)字段的類(lèi)和關(guān)鍵字參數(shù)。這些方法都應(yīng)該返回兩個(gè) (field_class, field_kwargs)元祖。
調(diào)用后生成對(duì)應(yīng)標(biāo)準(zhǔn)模型字段的序列化器字段。
默認(rèn)實(shí)現(xiàn)是根據(jù)serializer_field_mapping屬性返回一個(gè)序列化器類(lèi)。
調(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屬性。
當(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屬性。
被調(diào)用后生成一個(gè)對(duì)應(yīng)到模型類(lèi)中屬性或無(wú)參數(shù)方法的序列化器字段。
默認(rèn)實(shí)現(xiàn)是返回一個(gè)ReadOnlyField類(lèi)。
被調(diào)用后為序列化器自己的url字段生成一個(gè)序列化器字段。默認(rèn)實(shí)現(xiàn)是返回一個(gè)HyperlinkedIdentityField類(lèi)。
當(dāng)字段名稱(chēng)沒(méi)有對(duì)應(yīng)到任何模型字段或者模型屬性時(shí)調(diào)用。 默認(rèn)實(shí)現(xiàn)會(huì)拋出一個(gè)錯(cuò)誤,盡管子類(lèi)可能會(huì)自定義該行為。
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')
當(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)默認(rèn)為'url'。你可以通過(guò)使用URL_FIELD_NAME設(shè)置進(jìn)行全局性修改。
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ù)的序列化器。
默認(rèn)是True,但是如果你不想把空列表當(dāng)作有效輸入的話(huà)可以把它設(shè)置成False。
下面是你可能希望要定制ListSerializer行為的一些情況。例如:
對(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)建默認(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
默認(rèn)情況下,ListSerializer類(lèi)不支持多對(duì)象的更新。這是因?yàn)椴迦牒蛣h除的預(yù)期行為是不明確的。
要支持多對(duì)象更新的話(huà)你需要自己明確地實(shí)現(xiàn)。編寫(xiě)多個(gè)對(duì)象更新的代碼時(shí)要注意以下幾點(diǎn):
你需要向?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版本的第三方包中提供。
當(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 可以很簡(jiǎn)單的用來(lái)替代序列化和反序列化的樣式。
該類(lèi)實(shí)現(xiàn)與Serializer類(lèi)相同的基本API:
它還有可以覆寫(xiě)的四種方法,具體取決于你想要序列化類(lèi)支持的功能:
因?yàn)榇祟?lèi)提供與Serializer類(lèi)相同的接口,所以你可以將它與現(xiàn)有的基于類(lèi)的通用視圖一起使用,就像使用常規(guī)Serializer或ModelSerializer一樣。
這樣做時(shí)你需要注意到的唯一區(qū)別是BaseSerializer類(lèi)并不會(huì)在可瀏覽的API頁(yè)面中生成HTML表單。
要使用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)
要?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)
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)
更多建議: