Serializers
序列化器允許將諸如查詢集和模型實例之類的復雜數(shù)據(jù)轉(zhuǎn)換為原生 Python 數(shù)據(jù)類型壶辜,然后可以將它們輕松地呈現(xiàn)為 JSON
程癌,XML
或其他內(nèi)容類型。序列化器還提供反序列化沿癞,在首次驗證傳入數(shù)據(jù)之后援雇,可以將解析的數(shù)據(jù)轉(zhuǎn)換回復雜類型。
REST framework 中的序列化類與 Django 的 Form
和 ModelForm
類非常相似椎扬。我們提供了一個 Serializer
類惫搏,它提供了一種強大的通用方法來控制響應的輸出,以及一個 ModelSerializer
類蚕涤,它為創(chuàng)建處理模型實例和查詢集的序列化提供了有效的快捷方式晶府。
申明序列化類
首先創(chuàng)建一個簡單的對象用于示例:
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')
聲明一個序列化類,使用它來序列化和反序列化與 Comment
對象相對應的數(shù)據(jù)钻趋。
聲明一個序列化類看起來非常類似于聲明一個表單:
from rest_framework import serializers
class CommentSerializer(serializers.Serializer):
email = serializers.EmailField()
content = serializers.CharField(max_length=200)
created = serializers.DateTimeField()
序列化對象
現(xiàn)在可以使用 CommentSerializer
來序列化評論或評論列表川陆。同樣,使用 Serializer
類看起來很像使用 Form
類蛮位。
serializer = CommentSerializer(comment)
serializer.data
# {'email': 'leila@example.com', 'content': 'foo bar', 'created': '2016-01-27T15:17:10.375877'}
此時已經(jīng)將模型實例轉(zhuǎn)換為 Python 原生數(shù)據(jù)類型较沪。為了完成序列化過程,將數(shù)據(jù)渲染為 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"}'
反序列化對象
反序列化是相似的尸曼。首先我們將一個流解析為 Python 原生數(shù)據(jù)類型...
from django.utils.six import BytesIO
from rest_framework.parsers import JSONParser
stream = BytesIO(json)
data = JSONParser().parse(stream)
...然后我們將這些原生數(shù)據(jù)類型恢復成通過驗證的數(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ù)據(jù)返回完整的對象實例萄焦,則需要實現(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
如果對象實例與 Django 模型相對應冤竹,還需要確保這些方法將對象保存到數(shù)據(jù)庫。如果 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ù)時,我們可以調(diào)用 .save()
根據(jù)驗證的數(shù)據(jù)返回一個對象實例在抛。
comment = serializer.save()
調(diào)用 .save()
將創(chuàng)建一個新實例或更新現(xiàn)有實例钟病,具體取決于在實例化序列化類時是否傳遞了現(xiàn)有實例:
# .save() will create a new instance.
serializer = CommentSerializer(data=data)
# .save() will update the existing `comment` instance.
serializer = CommentSerializer(comment, data=data)
.create()
和 .update()
方法都是可選的。您可以都不實現(xiàn)刚梭,或者實現(xiàn)其中的一個或兩個肠阱,具體取決于你的序列化類的用例。
將附加屬性傳遞給 .save()
有時你會希望你的視圖代碼能夠在保存實例的時候注入額外的數(shù)據(jù)朴读。這些附加數(shù)據(jù)可能包含當前用戶屹徘,當前時間或其他任何不屬于請求數(shù)據(jù)的信息。
serializer.save(owner=request.user)
調(diào)用 .create()
或 .update()
時衅金,任何其他關(guān)鍵字參數(shù)都將包含在 validated_data
參數(shù)中噪伊。
直接覆蓋 .save()
。
在某些情況下典挑,.create()
和 .update()
方法名稱可能沒有意義酥宴。例如,在 “聯(lián)系人表單” 中您觉,我們可能不會創(chuàng)建新實例拙寡,而是發(fā)送電子郵件或其他消息。
在這些情況下琳水,可以選擇直接覆蓋 .save()
肆糕,因為它更具可讀性和有意義性。
舉個栗子:
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)
請注意在孝,在上面的情況下诚啃,必須直接訪問 serializer .validated_data
屬性。
驗證
在反序列化數(shù)據(jù)時私沮,你總是需要在嘗試訪問驗證數(shù)據(jù)之前調(diào)用 is_valid()
始赎,或者保存對象實例。如果發(fā)生任何驗證錯誤仔燕,那么 .errors
屬性將包含一個代表錯誤消息的字典造垛。例如:
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.']}
字典中的每個鍵都是字段名稱,值是與該字段相對應的錯誤消息(字符串列表)晰搀。non_field_errors
鍵也可能存在五辽,并會列出任何常規(guī)驗證錯誤⊥馑。可以使用 NON_FIELD_ERRORS_KEY
(在 settings 文件中設(shè)置)來定制 non_field_errors
關(guān)鍵字的名稱杆逗。
反序列化 item 列表時乡翅,錯誤將作為代表每個反序列化 item 的字典列表返回。
數(shù)據(jù)驗證時拋出異常
.is_valid()
方法帶有一個可選的 raise_exception
標志罪郊,如果存在驗證錯誤蠕蚜,將導致它引發(fā) serializers.ValidationError
異常。
這些異常由 REST framework 提供的默認異常處理程序自動處理排龄,并且默認情況下將返回 HTTP 400 Bad Request
波势。
# Return a 400 response if the data was invalid.
serializer.is_valid(raise_exception=True)
字段級驗證
你可以通過向 Serializer
子類添加 .validate_<field_name>
方法來指定自定義字段級驗證翎朱。這些與 Django 表單上的 .clean_<field_name>
方法類似橄维。
這些方法只有一個參數(shù),就是需要驗證的字段值拴曲。
您的 validate_<field_name>
方法應返回驗證值或引發(fā) 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
,那么如果未包含該字段澈灼,則不會執(zhí)行此驗證步驟竞川。
對象級驗證
如果要對多個字段進行其他的驗證,請將一個名為 .validate()
的方法添加到您的 Serializer
子類中叁熔。這個方法只有一個參數(shù)委乌,它是一個字段值(field
-value
)的字典。如果有必要荣回,它應該引發(fā)一個 ValidationError
遭贸,或者只是返回驗證的值。例如:
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
驗證器
序列化器上的各個字段可以包含驗證器心软,方法是在字段實例上聲明它們壕吹,例如:
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])
...
序列化類還可以包含應用于整個字段數(shù)據(jù)集的可重用驗證器。這些驗證器是通過在內(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)系哦耳贬,更多關(guān)于驗證的內(nèi)容,以后還會說到猎唁。
訪問初始數(shù)據(jù)和實例
將初始對象或查詢集傳遞給序列化類實例時咒劲,該對象將作為 .instance
提供。如果沒有傳遞初始對象诫隅,則 .instance
屬性將為 None
腐魂。
將數(shù)據(jù)傳遞給序列化類實例時,未修改的數(shù)據(jù)將作為 .initial_data
提供阎肝。如果 data 關(guān)鍵字參數(shù)未被傳遞挤渔,那么 .initial_data
屬性將不存在。
部分更新
默認情況下风题,序列化程序必須為所有必填字段傳遞值判导,否則會引發(fā)驗證錯誤嫉父。您可以使用 partial
參數(shù)以允許部分更新。
# Update `comment` with partial data
serializer = CommentSerializer(comment, data={'content': u'foo bar'}, partial=True)
處理嵌套對象
前面的例子適用于處理只具有簡單數(shù)據(jù)類型的對象眼刃,但有時還需要能夠表示更復雜的對象绕辖,其中對象的某些屬性可能不是簡單的數(shù)據(jù)類型,如字符串擂红,日期或整數(shù)仪际。
Serializer
類本身就是一種 Field
,可以用來表示一個對象類型嵌套在另一個對象類型中的關(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
值树碱,則應將 required = False
標志傳遞給嵌套的序列化類。
class CommentSerializer(serializers.Serializer):
user = UserSerializer(required=False) # May be an anonymous user.
content = serializers.CharField(max_length=200)
created = serializers.DateTimeField()
同樣变秦,如果嵌套對象是一個列表成榜,則應將 many = True
標志傳遞給嵌套的序列化類。
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()
可寫嵌套表示
在處理支持反序列化數(shù)據(jù)的嵌套表示時蹦玫,嵌套對象的任何錯誤都將嵌套在嵌套對象的字段名稱下赎婚。
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)樱溉。
為嵌套表示書寫 .create()
方法
如果你支持可寫嵌套表示挣输,則需要編寫處理保存多個對象的 .create()
或 .update()
方法。
以下示例演示如何處理使用嵌套配置文件對象創(chuàng)建用戶福贞。
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
為嵌套表示書寫 .update()
方法
對于更新撩嚼,您需要仔細考慮如何處理關(guān)系更新蚊锹。例如贩幻,如果關(guān)系的數(shù)據(jù)是 None
或沒有提供,則應發(fā)生以下哪種情況睡互?
- 在數(shù)據(jù)庫中將關(guān)系設(shè)置為
NULL
肠套。 - 刪除關(guān)聯(lián)的實例舰涌。
- 忽略數(shù)據(jù)并保持原樣。
- 引發(fā)驗證錯誤你稚。
以下是我們以前的 UserSerializer
類中的 .update()
方法的示例瓷耙。
def update(self, instance, validated_data):
profile_data = validated_data.pop('profile')
# Unless the application properly enforces that this field is
# always set, the follow could raise a `DoesNotExist`, which
# would need to be handled.
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
因為嵌套創(chuàng)建和更新的行為可能不明確,并且可能需要相關(guān)模型之間的復雜依賴關(guān)系刁赖,所以 REST framework 3 要求你始終明確寫入這些方法搁痛。默認的 ModelSerializer
的 .create()
和 .update()
方法不包括對可寫嵌套表示的支持。
不過宇弛,有第三方軟件包可用鸡典,如支持自動可寫嵌套表示的 DRF Writable Nested。
在模型管理器類中保存相關(guān)的實例
在序列化類中保存多個相關(guān)實例的另一種方法是編寫自定義模型管理器類枪芒。
例如彻况,假設(shè)我們希望確保 User
實例和 Profile
實例始終作為一對創(chuàng)建谁尸。我們可能會編寫一個類似下面的自定義管理器類:
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
此管理器類現(xiàn)在更好地封裝了用戶實例和配置文件實例始終在同一時間創(chuàng)建。現(xiàn)在可以重新編寫序列化類上的 .create()
方法纽甘,以使用新的管理類方法良蛮。
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']
)
處理多個對象
Serializer
類還可以處理序列化或反序列化對象列表。
序列化多個對象
要序列化查詢集或?qū)ο罅斜矶皇菃蝹€對象實例悍赢,在實例化序列化類時决瞳,應該傳遞 many=True
標志。然后左权,您可以傳遞要序列化的查詢集或?qū)ο罅斜怼?/p>
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'}
# ]
反序列化多個對象
反序列化多個對象的默認行為是支持多個對象創(chuàng)建皮胡,但不支持多個對象更新。
包含額外的上下文
除了被序列化的對象外涮总,還有一些情況需要為序列化類提供額外的上下文胸囱。一種常見的情況是祷舀,如果你使用的是包含超鏈接關(guān)系的序列化類瀑梗,則需要序列化類訪問當前請求,以便它可以正確生成完全限定的URL裳扯。
在實例化序列化對象時抛丽,你可以通過傳遞上下文參數(shù)來提供任意附加上下文。例如:
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'}
通過訪問 self.context
屬性饰豺,可以在任何序列化對象字段邏輯中使用上下文字典亿鲜,例如自定義的 .to_representation()
方法。
ModelSerializer
通常你會想要序列化類緊密地映射到 Django 模型定義上冤吨。
ModelSerializer
類提供了一個快捷方式蒿柳,可讓你自動創(chuàng)建一個 Serializer
類,其中的字段與模型類字段對應漩蟆。
ModelSerializer
類與常規(guī) Serializer
類相同垒探,不同之處在于:
- 它會根據(jù)模型自動生成一組字段。
- 它會自動為序列化類生成驗證器怠李,例如 unique_together 驗證器圾叼。
- 它包含
.create()
和.update()
的簡單默認實現(xiàn)。
聲明ModelSerializer如下所示:
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
fields = ('id', 'account_name', 'users', 'created')
默認情況下捺癞,該類中的所有模型類字段將被映射為相應的序列化類字段夷蚊。
任何關(guān)系(如模型上的外鍵)都將映射到 PrimaryKeyRelatedField
。除非在序列化關(guān)系文檔中指定髓介,否則默認不包括反向關(guān)系惕鼓。
檢查 ModelSerializer
序列化類能夠生成一個表示字符串,可以讓你充分檢查其字段的狀態(tài)唐础。在使用 ModelSerializer
進行工作時箱歧,這是特別有用的夫否,你需要確定它為你自動創(chuàng)建了哪些字段和驗證器。
為此叫胁,使用 python manage.py shell
進入 Django shell凰慈,然后導入序列化類,實例化它并打印對象表示形式...
>>> 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())
指定要包含的字段
如果你只希望在模型序列化程序中使用默認字段的子集驼鹅,則可以使用 fields
或 exclude
選項來完成此操作微谓,就像使用 ModelForm
一樣。強烈建議你顯式使用 fields
屬性序列化的所有字段输钩。這將使你不太可能在模型更改時無意中暴露數(shù)據(jù)豺型。
舉個栗子:
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
fields = ('id', 'account_name', 'users', 'created')
你還可以將 fields
屬性設(shè)置為特殊值 '__all__'
,以指示應該使用模型中的所有字段买乃。
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
fields = '__all__'
你可以將 exclude
屬性設(shè)置為從序列化程序中排除的字段列表姻氨。
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
exclude = ('users',)
在上面的示例中,如果 Account
模型有3個字段 account_name
剪验,users
和 created
肴焊,則會導致字段 account_name
和 created
被序列化。
fields
和 exclude
屬性中的名稱通常映射到模型類的模型字段功戚。
或者fields
選項中的名稱可以映射成屬性或方法娶眷。而不會變成模型類中的參數(shù)。
從版本 3.3.0 開始啸臀,必須提供其中一個屬性 fields
或 exclude
届宠。
指定嵌套序列化
默認的 ModelSerializer
使用主鍵進行關(guān)聯(lián),但你也可以使用 depth
選項輕松生成嵌套表示(自關(guān)聯(lián)):
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
fields = ('id', 'account_name', 'users', 'created')
depth = 1
depth
選項應設(shè)置為一個整數(shù)值乘粒,該值指示在還原為平面表示之前應該遍歷的關(guān)聯(lián)的深度豌注。
如果你想自定義序列化的方式,你需要自己定義字段灯萍。
顯式指定字段
你可以將額外的字段添加到 ModelSerializer
轧铁,或者通過在類上聲明字段來覆蓋默認字段,就像你對 Serializer
類所做的那樣竟稳。
class AccountSerializer(serializers.ModelSerializer):
url = serializers.CharField(source='get_absolute_url', read_only=True)
groups = serializers.PrimaryKeyRelatedField(many=True)
class Meta:
model = Account
額外的字段可以對應于模型上的任何屬性或可調(diào)用的字段属桦。
指定只讀字段
你可能希望將多個字段指定為只讀。不要顯式給每個字段添加 read_only = True
屬性他爸,你可以使用快捷方式 Meta 選項 read_only_fields
聂宾。
該選項應該是字段名稱的列表或元組,聲明如下:
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
fields = ('id', 'account_name', 'users', 'created')
read_only_fields = ('account_name',)
含有 editable = False
的模型字段诊笤,AutoField
字段默認設(shè)置為只讀系谐,并且不需要添加到 read_only_fields
選項。
注意: 有一種特殊情況,只讀字段是模型級別的
unique_together
約束的一部分纪他。在這種情況下鄙煤,序列化類需要驗證約束該字段,但也不能由用戶編輯茶袒。
處理這個問題的正確方法是在序列化類中明確指定字段梯刚,同時提供
read_only = True
和default = ...
關(guān)鍵字參數(shù)。
其中一個例子是與當前認證
User
的只讀關(guān)系薪寓,它與另一個標識符是unique_together
亡资。在這種情況下,你會像這樣聲明用戶字段:
user = serializers.PrimaryKeyRelatedField(read_only=True, default=serializers.CurrentUserDefault())
關(guān)于驗證以后還會再說
其他關(guān)鍵字參數(shù)
還有一個快捷方式允許你使用 extra_kwargs
選項在字段上指定任意附加關(guān)鍵字參數(shù)向叉。與 read_only_fields
的情況一樣锥腻,這意味著你不需要在序列化類中顯式聲明該字段。
該選項是一個字典母谎,將字段名稱映射到關(guān)鍵字參數(shù)字典瘦黑。例如:
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)系字段
序列化模型實例時,可以選擇多種不同的方式來表示關(guān)系奇唤。ModelSerializer
的默認表示是使用相關(guān)實例的主鍵幸斥。
其他表示形式包括使用超鏈接序列化,序列化完整嵌套表示形式或使用自定義表示形式序列化冻记。
自定義字段映射
ModelSerializer 類還公開了一個可以覆蓋的 API睡毒,以便在實例化序列化對象時更改序列化對象的字段。
通常冗栗,如果 ModelSerializer
沒辦法生成默認需要的字段,那么你應該將它們明確地添加到類中供搀,或者直接使用常規(guī)的Serializer
類隅居。但是,在某些情況下葛虐,你可能需要創(chuàng)建一個新的基類胎源,以定義如何為給定模型創(chuàng)建序列化對象的字段。
.serializer_field_mapping
Django 模型類到 REST framework 序列化類的映射屿脐。你可以重寫此映射來更改應該用于每個模型類的默認序列化類涕蚤。
.serializer_related_field
該屬性應該是序列化器字段類,默認情況下用于關(guān)系字段的诵。
對于 ModelSerializer
万栅,它默認為 PrimaryKeyRelatedField
。
對于 HyperlinkedModelSerializer
西疤,它默認為 serializers.HyperlinkedRelatedField
烦粒。
serializer_url_field
序列化器字段類,應該用于序列化類中的任何 url
字段。
默認是 serializers.HyperlinkedIdentityField
扰她。
serializer_choice_field
序列化器字段類兽掰,應該用于序列化程序中的任何選擇字段。
默認是 serializers.ChoiceField
徒役。
field_class 和 field_kwargs API
調(diào)用以下方法來確定應該自動包含在序列化程序中的每個字段的類和關(guān)鍵字參數(shù)孽尽。這些方法都應返回兩個元組 (field_class, field_kwargs)
。
.build_standard_field(self, field_name, model_field)
調(diào)用以生成映射到標準模型字段的序列化器字段忧勿。
默認實現(xiàn)基于 serializer_field_mapping
屬性返回序列化類泻云。
.build_relational_field(self, field_name, relation_info)
調(diào)用以生成映射到關(guān)系模型字段的序列化器字段。
默認實現(xiàn)基于 serializer_relational_field
屬性返回一個序列化類狐蜕。
relation_info
參數(shù)是一個命名元組宠纯,它包含 model_field
,related_model
层释,to_many
和 has_through_model
屬性婆瓜。
.build_nested_field(self, field_name, relation_info, nested_depth)
當 depth
選項已設(shè)置時,調(diào)用以生成映射到關(guān)系模型字段的序列化程序字段贡羔。
默認實現(xiàn)動態(tài)創(chuàng)建一個基于 ModelSerializer
或 HyperlinkedModelSerializer
的嵌套序列化類廉白。
nested_depth
將是 depth
選項的值減 1。
relation_info
參數(shù)是一個命名元組乖寒,它包含 model_field
猴蹂,related_model
,to_many
和 has_through_model
屬性楣嘁。
.build_property_field(self, field_name, model_class)
調(diào)用以生成映射到模型類上的屬性或零參數(shù)方法的序列化器字段磅轻。
默認實現(xiàn)返回一個 ReadOnlyField
類。
.build_url_field(self, field_name, model_class)
被調(diào)用來為序列化器自己的 url
字段生成一個序列化器字段逐虚。
默認實現(xiàn)返回一個 HyperlinkedIdentityField
類聋溜。
.build_unknown_field(self, field_name, model_class)
當字段名稱未映射到任何模型字段或模型屬性時調(diào)用。默認實現(xiàn)會引發(fā)錯誤叭爱。但是子類可以自定義這種行為撮躁。
HyperlinkedModelSerializer
HyperlinkedModelSerializer
類與 ModelSerializer
類相似,只不過它使用超鏈接來表示關(guān)系而不是主鍵买雾。
默認情況下把曼,序列化器將包含一個 url
字段而不是主鍵字段。
url 字段將使用 HyperlinkedIdentityField
序列化器字段來表示漓穿,并且模型上的任何關(guān)系都將使用 HyperlinkedRelatedField
序列化器字段來表示嗤军。
你可以通過將主鍵添加到 fields
選項來明確包含主鍵,例如:
class AccountSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Account
fields = ('url', 'id', 'account_name', 'users', 'created')
絕對和相對 URL
在實例化 HyperlinkedModelSerializer
時器净,必須在序列化上下文中包含當前請求型雳,例如:
serializer = AccountSerializer(queryset, context={'request': request})
這樣做將確保超鏈接可以包含適當?shù)闹鳈C名,以便生成完全限定的 URL,例如:
http://api.example.com/accounts/1/
而不是相對的 URL纠俭,例如:
/accounts/1/
如果你確實想要使用相對 URL沿量,則應該在序列化上下文中顯式傳遞 {'request':None}
。
如何確定超鏈接視圖
需要確定哪些視圖應該用于超鏈接到模型實例冤荆。
默認情況下朴则,超鏈接預期對應于與樣式 '{model_name}-detail'
匹配的視圖名稱,并通過 pk
關(guān)鍵字參數(shù)查找實例钓简。
您可以使用 extra_kwargs
設(shè)置中的 view_name
和 lookup_field
選項覆蓋 URL 字段視圖名稱和查找字段乌妒,如下所示:
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 conf 有時可能有點困難撤蚊。打印
HyperlinkedModelSerializer
實例的repr
是一種特別有用的方法,可以準確檢查這些關(guān)系預期映射的 view name 和 lookup field损话。
更改 URL 字段名稱
URL 字段的名稱默認為 'url'侦啸。可以使用 URL_FIELD_NAME
(在 settings 文件)全局覆蓋此設(shè)置丧枪。
ListSerializer
ListSerializer
類提供了一次序列化和驗證多個對象的行為光涂。你通常不需要直接使用 ListSerializer
,而應該在實例化序列化類時簡單地傳遞 many=True
拧烦。
當一個序列化類被實例化并且 many = True
被傳遞時忘闻,一個 ListSerializer
實例將被創(chuàng)建。序列化類成為父級 ListSerializer
的子級
以下參數(shù)也可以傳遞給 ListSerializer
字段或傳遞了 many = True
的序列化類:
allow_empty
默認情況下為 True
恋博,但如果要禁止將空列表作為有效輸入齐佳,則可將其設(shè)置為 False
。
自定義 ListSerializer
行為
有幾種情況可能需要自定義 ListSerializer
行為交播。例如:
- 希望提供對列表的特定驗證重虑,例如檢查一個元素是否與列表中的另一個元素不沖突。
- 想要自定義多個對象的創(chuàng)建或更新行為秦士。
對于這些情況,可以通過使用序列化類的 Meta
類中的 list_serializer_class
選項來修改傳遞了 many=True
時使用的類永高。
class CustomListSerializer(serializers.ListSerializer):
...
class CustomSerializer(serializers.Serializer):
...
class Meta:
list_serializer_class = CustomListSerializer
自定義多個對象的創(chuàng)建
創(chuàng)建多個對象的默認實現(xiàn)是簡單地為列表中的每個 item 調(diào)用 .create()
隧土。如果要自定義此行為,則需要在傳遞 many=True
時自定義 ListSerializer
類上的 .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
自定義多個對象的更新
默認情況下曹傀,ListSerializer
類不支持多對象更新。這是因為插入和刪除預期的行為是不明確的饲宛。
為了支持多對象更新皆愉,你需要重寫 update 方法。在編寫你的多對象更新代碼時,一定要記住以下幾點:
- 如何確定應該為數(shù)據(jù)列表中的每個 item 更新哪個實例幕庐?
- 插入應該如何處理久锥?它們是無效的,還是創(chuàng)建新對象异剥?
- 應該如何處理刪除瑟由?它們是否暗示了對象刪除冤寿,或者刪除了一段關(guān)系歹苦?它們應該被忽略,還是無效督怜?
- 如何處理排序殴瘦?改變兩個 item 的位置是否意味著狀態(tài)的改變或者被忽略?
你需要為實例序列化類添加一個顯式 id
字段号杠。默認的隱式生成的 id
字段被標記為 read_only
蚪腋。這會導致它在更新時被刪除。一旦你明確聲明它究流,它將在列表序列化類的更新方法中可用辣吃。
下面是你如何選擇實現(xiàn)多對象更新的示例:
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):
# We need to identify elements in the list using their primary key,
# so use a writable field here, rather than the default which would be read-only.
id = serializers.IntegerField()
...
class Meta:
list_serializer_class = BookListSerializer
自定義 ListSerializer 初始化
當具有 many=True
的序列化類實例化時,我們需要確定哪些參數(shù)和關(guān)鍵字參數(shù)應該傳遞給子級 Serializer
類和父級 ListSerializer
類的 .__ init __()
方法芬探。
默認的實現(xiàn)是將所有參數(shù)傳遞給兩個類神得,除了 validators
和任何自定義關(guān)鍵字參數(shù),這兩個參數(shù)都假定用于子序列化類偷仿。
有時你可能需要明確指定在傳遞 many=True
時如何實例化子類和父類哩簿。您可以使用 many_init
類方法來完成此操作。
@classmethod
def many_init(cls, *args, **kwargs):
# Instantiate the child serializer.
kwargs['child'] = cls()
# Instantiate the parent list serializer.
return CustomListSerializer(*args, **kwargs)
BaseSerializer
BaseSerializer
類可以用來方便地支持其他序列化和反序列化風格酝静。
這個類實現(xiàn)了與 Serializer
類相同的基本 API:
-
.data
- 返回傳出的原始表示节榜。 -
.is_valid()
- 反序列化并驗證傳入的數(shù)據(jù)。 -
.validated_data
- 返回驗證的傳入數(shù)據(jù)别智。 -
.errors
- 在驗證期間返回錯誤宗苍。 -
.save()
- 將驗證的數(shù)據(jù)保存到對象實例中。
有四種方法可以被覆蓋薄榛,這取決于你希望序列化類支持的功能:
-
.to_representation()
- 重寫此操作以支持序列化讳窟,用于讀取操作。 -
.to_internal_value()
- 重寫此操作以支持反序列化敞恋,以用于寫入操作丽啡。 -
.create() 和 .update()
- 覆蓋其中一個或兩個以支持保存實例。
因為這個類提供了與 Serializer
類相同的接口硬猫,所以你可以像現(xiàn)有的常規(guī) Serializer
或 ModelSerializer
一樣补箍,將它與基于類的通用視圖一起使用改执。
唯一不同的是,BaseSerializer
類不會在可瀏覽的 API 中生成 HTML 表單坑雅。這是因為它們返回的數(shù)據(jù)不包含所有的字段信息辈挂,這些字段信息允許將每個字段渲染為合適的 HTML 輸入。
Read-only BaseSerializer
類
要使用 BaseSerializer
類實現(xiàn)只讀序列化類霞丧,我們只需重寫 .to_representation()
方法呢岗。讓我們來看一個使用簡單的 Django 模型的示例:
class HighScore(models.Model):
created = models.DateTimeField(auto_now_add=True)
player_name = models.CharField(max_length=10)
score = models.IntegerField()
創(chuàng)建用于將 HighScore
實例轉(zhuǎn)換為基本數(shù)據(jù)類型的只讀序列化類非常簡單。
class HighScoreSerializer(serializers.BaseSerializer):
def to_representation(self, obj):
return {
'score': obj.score,
'player_name': obj.player_name
}
我們現(xiàn)在可以使用這個類來序列化單個 HighScore
實例:
@api_view(['GET'])
def high_score(request, pk):
instance = HighScore.objects.get(pk=pk)
serializer = HighScoreSerializer(instance)
return Response(serializer.data)
或者用它來序列化多個實例:
@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 類
要創(chuàng)建一個可讀寫的序列化類蛹尝,我們首先需要實現(xiàn)一個 .to_internal_value()
方法后豫。此方法返回將用于構(gòu)造對象實例的驗證值,并且如果提供的數(shù)據(jù)格式不正確突那,則可能引發(fā) ValidationError
挫酿。
一旦實現(xiàn) .to_internal_value()
,基本驗證 API 將在序列化器中可用愕难,并且你將能夠使用 .is_valid()
早龟,.validated_data
和 .errors
。
如果你還想支持 .save()
猫缭,則還需要實現(xiàn) .create()
和 .update()
方法中的一個或兩個葱弟。
以下是我們之前的 HighScoreSerializer
的一個完整示例,該示例已更新為支持讀取和寫入操作猜丹。
class HighScoreSerializer(serializers.BaseSerializer):
def to_internal_value(self, data):
score = data.get('score')
player_name = data.get('player_name')
# Perform the data validation.
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.'
})
# Return the validated values. This will be available as
# the `.validated_data` property.
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)建新的基類
如果你希望實現(xiàn)新的泛型序列化類來處理特定的序列化風格芝加,或者與可選的存儲后端進行集成,那么 BaseSerializer
類也很有用射窒。
以下類是可以處理將任意對象強制轉(zhuǎn)換為基本表示形式的泛型序列化類的示例藏杖。
class ObjectSerializer(serializers.BaseSerializer):
"""
A read-only serializer that coerces arbitrary complex objects
into primitive representations.
"""
def to_representation(self, obj):
for attribute_name in dir(obj):
attribute = getattr(obj, attribute_name)
if attribute_name('_'):
# Ignore private attributes.
pass
elif hasattr(attribute, '__call__'):
# Ignore methods and other callables.
pass
elif isinstance(attribute, (str, int, bool, float, type(None))):
# Primitive types can be passed through unmodified.
output[attribute_name] = attribute
elif isinstance(attribute, list):
# Recursively deal with items in lists.
output[attribute_name] = [
self.to_representation(item) for item in attribute
]
elif isinstance(attribute, dict):
# Recursively deal with items in dictionaries.
output[attribute_name] = {
str(key): self.to_representation(value)
for key, value in attribute.items()
}
else:
# Force anything else to its string representation.
output[attribute_name] = str(attribute)
Serializer 使用進階
重寫序列化和反序列化行為
如果你需要更改序列化類的序列化或反序列化行為,可以通過覆蓋 .to_representation()
或 .to_internal_value()
方法來實現(xiàn)脉顿。
以下原因可能需要重寫這兩個方法...
- 為新的序列化基類添加新行為蝌麸。
- 稍微修改現(xiàn)有類的行為。
- 提高經(jīng)常訪問的 API 端點的序列化性能艾疟,以便返回大量數(shù)據(jù)来吩。
這些方法的簽名如下:
.to_representation(self, obj)
接受需要序列化的對象實例,并返回一個原始表示蔽莱。通常這意味著返回一個內(nèi)置 Python 數(shù)據(jù)類型的結(jié)構(gòu)误褪。可以處理的確切類型取決于您為 API 配置的渲染類碾褂。
可能會被重寫以便修改表示風格。例如:
def to_representation(self, instance):
"""Convert `username` to lowercase."""
ret = super().to_representation(instance)
ret['username'] = ret['username'].lower()
return ret
.to_internal_value(self, data)
將未驗證的傳入數(shù)據(jù)作為輸入历葛,并應返回將作為 serializer.validated_data
提供的驗證數(shù)據(jù)正塌。如果在序列化類上調(diào)用了 .save()
嘀略,則返回值也將傳遞給 .create()
或 .update()
方法。
如果驗證失敗乓诽,則該方法會引發(fā) serializers.ValidationError(errors)
帜羊。errors
參數(shù)應該是一個由字段名稱(或 settings.NON_FIELD_ERRORS_KEY
)映射到錯誤消息列表的字典。如果不需要改變反序列化行為鸠天,而是想提供對象級驗證讼育,則建議改為覆蓋 .validate()
方法。
傳遞給此方法的 data
參數(shù)通常是 request.data
的值稠集,因此它提供的數(shù)據(jù)類型將取決于你為 API 配置的解析器類奶段。
繼承序列化類
與 Django 表單類似,你可以通過繼承來擴展和重用序列化類剥纷。這使你可以在父類上聲明一組通用的字段或方法痹籍,然后可以在多個序列化類中使用它們。例如晦鞋,
class MyBaseSerializer(Serializer):
my_field = serializers.CharField()
def validate_my_field(self):
...
class MySerializer(MyBaseSerializer):
...
與 Django 的 Model
和 ModelForm
類一樣蹲缠,序列化類中的內(nèi)部 Meta
類不會從其父類的內(nèi)部 Meta
類中隱式繼承。如果你想讓 Meta
類繼承父類悠垛,必須明確的指出线定。例如:
class AccountSerializer(MyBaseSerializer):
class Meta(MyBaseSerializer.Meta):
model = Account
通常我們建議不要在內(nèi)部的 Meta
類中使用繼承,而是顯式聲明所有選項确买。
動態(tài)修改字段
一旦序列化類初始化完畢斤讥,就可以使用 .fields
屬性訪問在序列化類中設(shè)置的字段字典。通過訪問和修改這個屬性可以達到動態(tài)地修改序列化類的目的拇惋。
直接修改 fields
參數(shù)允許你做一些有趣的事情周偎,比如在運行時改變序列化字段的參數(shù),而不是在聲明序列化類的時候撑帖。
舉個栗子:
例如蓉坎,如果你希望能夠設(shè)置序列化類在初始化時應使用哪些字段,你可以創(chuàng)建這樣一個序列化類胡嘿,如下所示:
class DynamicFieldsModelSerializer(serializers.ModelSerializer):
"""
A ModelSerializer that takes an additional `fields` argument that
controls which fields should be displayed.
"""
def __init__(self, *args, **kwargs):
# Don't pass the 'fields' arg up to the superclass
fields = kwargs.pop('fields', None)
# Instantiate the superclass normally
super(DynamicFieldsModelSerializer, self).__init__(*args, **kwargs)
if fields is not None:
# Drop any fields that are not specified in the `fields` argument.
allowed = set(fields)
existing = set(self.fields)
for field_name in existing - allowed:
self.fields.pop(field_name)
這將允許你執(zhí)行以下操作:
>>> class UserSerializer(DynamicFieldsModelSerializer):
>>> class Meta:
>>> model = User
>>> fields = ('id', 'username', 'email')
>>>
>>> print UserSerializer(user)
{'id': 2, 'username': 'jonwatts', 'email': 'jon@example.com'}
>>>
>>> print UserSerializer(user, fields=('id', 'email'))
{'id': 2, 'email': 'jon@example.com'}