擴展serializers的有用性是我們想要解決的問題。但是,這不是一個微不足道的問題,而是需要一些嚴肅的設計工作?!?Russell Keith-Magee, Django用戶組
序列化器允許把像查詢集和模型實例這樣的復雜數(shù)據(jù)轉換為可以輕松渲染成JSON,XML或其他內容類型的原生Python類型。序列化器還提供反序列化,在驗證傳入的數(shù)據(jù)之后允許解析數(shù)據(jù)轉換回復雜類型。
REST framework中的serializers與Django的Form和ModelForm類非常像。我們提供了一個Serializer類,它為你提供了強大的通用方法來控制響應的輸出,以及一個ModelSerializer類,它為創(chuàng)建用于處理模型實例和查詢集的序列化程序提供了有用的快捷實現(xiàn)方式。
讓我們從創(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ù)。
聲明一個序列化器看起來非常像聲明一個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去序列化一個comment或comment列表。同樣,使用Serializer類看起來很像使用Form類。
serializer = CommentSerializer(comment)
serializer.data
# {'email': 'leila@example.com', 'content': 'foo bar', 'created': '2016-01-27T15:17:10.375877'}
此時,我們將模型實例轉換為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)其中一個或全部實現(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ù)的時候,基于驗證過的數(shù)據(jù)我們可以調用.save()方法返回一個對象實例。
comment = serializer.save()
調用.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()方法都是可選的。你可以根據(jù)你序列化器類的用例不實現(xiàn)、實現(xiàn)它們之一或都實現(xiàn)。
有時你會希望你的視圖代碼能夠在保存實例時注入額外的數(shù)據(jù)。此額外數(shù)據(jù)可能包括當前用戶,當前時間或不是請求數(shù)據(jù)一部分的其他信息。
你可以通過在調用.save()時添加其他關鍵字參數(shù)來執(zhí)行此操作。例如:
serializer.save(owner=request.user)
在.create()或.update()被調用時,任何其他關鍵字參數(shù)將被包含在validated_data參數(shù)中。
在某些情況下.create()和.update()方法可能無意義。例如在contact form中,我們可能不會創(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)
請注意在上述情況下,我們現(xiàn)在不得不直接訪問serializer的.validated_data屬性。
反序列化數(shù)據(jù)的時候,你始終需要先調用is_valid()方法,然后再嘗試去訪問經(jīng)過驗證的數(shù)據(jù)或保存對象實例。如果發(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鍵可能存在,它將列出任何一般驗證錯誤信息。non_field_errors的名稱可以通過REST framework設置中的NON_FIELD_ERRORS_KEY來自定義。 當對對象列表進行序列化時,返回的錯誤是每個反序列化項的字典列表。
.is_valid()方法使用可選的raise_exception標志,如果存在驗證錯誤將會拋出一個serializers.ValidationError異常。
這些異常由REST framework提供的默認異常處理程序自動處理,默認情況下將返回HTTP 400 Bad Request響應。
# 如果數(shù)據(jù)無效就返回400響應
serializer.is_valid(raise_exception=True)
你可以通過向你的Serializer子類中添加.validate_<field_name>方法來指定自定義字段級別的驗證。這些類似于Django表單中的.clean_<field_name>方法。
這些方法采用單個參數(shù),即需要驗證的字段值。
你的validate_<field_name>方法應該返回一個驗證過的數(shù)據(jù)或者拋出一個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>的時候帶有required=False參數(shù),字段不被包含的時候這個驗證步驟就不會執(zhí)行。
要執(zhí)行需要訪問多個字段的任何其他驗證,請?zhí)砑右粋€.validate()方法到你的Serializer子類中。這個方法采用字段值字典的單個參數(shù),如果需要應該拋出一個 ValidationError異常,或者只是返回經(jīng)過驗證的值。例如:
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ù)的可重用的驗證器。這些驗證器要在內部的Meta類中聲明,如下所示:
class EventSerializer(serializers.Serializer):
name = serializers.CharField()
room_number = serializers.IntegerField(choices=[101, 102, 103, 201])
date = serializers.DateField()
class Meta:
# 每間屋子每天只能有1個活動。
validators = UniqueTogetherValidator(
queryset=ToDoItem.objects.all(),
fields=['room_number', 'date']
)
更多信息請參閱 validators文檔。
將初始化對象或者查詢集傳遞給序列化實例時,可以通過.instance訪問。如果沒有傳遞初始化對象,那么.instance屬性將是None。
將數(shù)據(jù)傳遞給序列化器實例時,未修改的數(shù)據(jù)可以通過.initial_data獲取。如果沒有傳遞data關鍵字參數(shù),那么.initial_data屬性就不存在。
默認情況下,序列化器必須傳遞所有必填字段的值,否則就會引發(fā)驗證錯誤。你可以使用 partial參數(shù)來允許部分更新。
# 使用部分數(shù)據(jù)更新`comment`
serializer = CommentSerializer(comment, data={'content': u'foo bar'}, partial=True)
前面的實例適用于處理只有簡單數(shù)據(jù)類型的對象,但是有時候我們也需要表示更復雜的對象,其中對象的某些屬性可能不是字符串、日期、整數(shù)這樣簡單的數(shù)據(jù)類型。
Serializer類本身也是一種Field,并且可以用來表示一個對象類型嵌套在另一個對象中的關系。
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) # 可能是匿名用戶。
content = serializers.CharField(max_length=200)
created = serializers.DateTimeField()
類似的,如果嵌套的關聯(lián)字段可以接收一個列表,那么應該將many=True標志傳遞給嵌套的序列化器。
class CommentSerializer(serializers.Serializer):
user = UserSerializer(required=False)
edits = EditItemSerializer(many=True) # edit'項的嵌套列表
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ù)結構。
如果你支持可寫的嵌套表示,則需要編寫.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
對于更新,你需要仔細考慮如何處理關聯(lián)字段的更新。 例如,如果關聯(lián)字段的值是None,或者沒有提供,那么會發(fā)生下面哪一項?
下面是我們之前UserSerializer類中update()方法的一個例子。
def update(self, instance, validated_data):
profile_data = validated_data.pop('profile')
# 除非應用程序正確執(zhí)行,
# 保證這個字段一直被設置,
# 否則就應該拋出一個需要處理的`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
因為嵌套關系的創(chuàng)建和更新行為可能不明確,并且可能需要關聯(lián)模型間的復雜依賴關系,REST framework 3 要求你始終明確的定義這些方法。默認的ModelSerializer .create()和.update()方法不包括對可寫嵌套關聯(lián)的支持。
提供自動支持某種類型的自動寫入嵌套關聯(lián)的第三方包可能與3.1版本一同放出。
在序列化器中保存多個相關實例的另一種方法是編寫處理創(chuàng)建正確實例的自定義模型管理器類。
例如,假設我們想確保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)建。我們在序列化器類上的.create()方法現(xiàn)在能夠用新的管理器方法重寫。
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']
)
有關此方法的更多詳細信息,請參閱Django文檔中的 模型管理器和使用模型和管理器類的相關博客。
Serializer類還可以序列化或反序列化對象的列表。
為了能夠序列化一個查詢集或者一個對象列表而不是一個單獨的對象,應該在實例化序列化器類的時候傳一個many=True參數(shù)。這樣就能序列化一個查詢集或一個對象列表。
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)建,但是不支持多個對象的更新。有關如何支持或自定義這些情況的更多信息,請查閱這個文檔ListSerializer。
在某些情況下,除了要序列化的對象之外,還需要為序列化程序提供額外的上下文。一個常見的情況是,如果你使用包含超鏈接關系的序列化程序,這需要序列化器能夠訪問當前的請求以便正確生成完全限定的URL。
你可以在實例化序列化器的時候傳遞一個context參數(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'}
這個上下文的字典可以在任何序列化器字段的邏輯中使用,例如.to_representation()方法中可以通過訪問self.context屬性獲取上下文字典。
通常你會想要與Django模型相對應的序列化類。
ModelSerializer類能夠讓你自動創(chuàng)建一個具有模型中相應字段的Serializer類。
這個ModelSerializer類和常規(guī)的Serializer類一樣,不同的是:
聲明一個ModelSerializer如下:
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
fields = ('id', 'account_name', 'users', 'created')
默認情況下,所有的模型的字段都將映射到序列化器上相應的字段。
模型中任何關聯(lián)字段比如外鍵都將映射到PrimaryKeyRelatedField字段。默認情況下不包括反向關聯(lián),除非像serializer relations文檔中規(guī)定的那樣顯式包含。
序列化類生成有用的詳細表示字符串,允許你全面檢查其字段的狀態(tài)。 這在使用ModelSerializers時特別有用,因為你想確定自動創(chuàng)建了哪些字段和驗證器。
要檢查的話,打開Django shell,執(zhí)行 python manage.py 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選項來執(zhí)行此操作,就像使用ModelForm一樣。強烈建議你使用fields屬性顯式的設置要序列化的字段。這樣就不太可能因為你修改了模型而無意中暴露了數(shù)據(jù)。
例如:
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
fields = ('id', 'account_name', 'users', 'created')
你還可以將fields屬性設置成'__all__'來表明使用模型中的所有字段。
例如:
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
fields = '__all__'
你可以將exclude屬性設置成一個從序列化器中排除的字段列表。
例如:
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
exclude = ('users',)
在上面的例子中,如果Account模型有三個字段account_name,users和created,那么只有 account_name和created會被序列化。
在fields和exclude屬性中的名稱,通常會映射到模型類中的模型字段。
或者fields選項中的名稱可以映射到模型類中不存在任何參數(shù)的屬性或方法。
默認ModelSerializer使用主鍵進行關聯(lián),但是你也可以使用depth選項輕松生成嵌套關聯(lián):
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
fields = ('id', 'account_name', 'users', 'created')
depth = 1
depth選項應該設置一個整數(shù)值,表明應該遍歷的關聯(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
額外的字段可以對應模型上任何屬性或可調用的方法。
你可能希望將多個字段指定為只讀,而不是顯式的為每個字段添加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',)
模型中已經(jīng)設置editable=False的字段和默認就被設置為只讀的AutoField字段都不需要添加到read_only_fields選項中。
注意: 有一種特殊情況,其中一個只讀字段是模型級別unique_together約束的一部分。在這種情況下,序列化器類需要該字段才能驗證約束,但也不能由用戶編輯。
處理此問題的正確方法是在序列化器上顯式指定該字段,同時提供read_only=True和default=…關鍵字參數(shù)。
這種情況的一個例子就是對于一個和其他標識符unique_together的當前認證的User是只讀的。 在這種情況下你可以像下面這樣聲明user字段:
user = serializers.PrimaryKeyRelatedField(read_only=True, default=serializers.CurrentUserDefault())
有關UniqueTogetherValidator和CurrentUserDefault類的詳細文檔,請查閱驗證器的文檔。
還可以通過使用extra_kwargs選項快捷地在字段上指定任意附加的關鍵字參數(shù)。在read_only_fields這種情況下,你不需要在序列化器上式的聲明該字段。
這個選項是一個將具體字段名稱當作鍵值的字典。例如:
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
在序列化模型實例的時候,你可以選擇多種不同的方式來表示關聯(lián)關系。對于ModelSerializer默認是使用相關實例的主鍵。
替代的其他方法包括使用超鏈接序列化,序列化完整的嵌套表示或者使用自定義表示的序列化。
更多詳細信息請查閱serializer relations文檔。
ModelSerializer類還公開了一個可以覆蓋的API,以便在實例化序列化器時改變序列化器字段的自動確定。
通常情況下,如果ModelSerializer沒有生成默認情況下你需要的字段,那么你應該將它們顯式地添加到類中,或者直接使用常規(guī)的Serializer類。但是在某些情況下,你可能需要創(chuàng)建一個新的基類,來定義給任意模型創(chuàng)建序列化字段的方式。
將Django model類映射到REST framework serializer類。你可以覆寫這個映射來更改每個模型應該使用的默認序列化器類。
這個屬性應是序列化器字段類,默認情況下用于關聯(lián)字段。
對于ModelSerializer此屬性默認是PrimaryKeyRelatedField。
對于HyperlinkedModelSerializer此屬性默認是serializers.HyperlinkedRelatedField。
應該用于序列化器上任何url字段的序列化器字段類。
默認是 serializers.HyperlinkedIdentityField
應用于序列化器上任何選擇字段的序列化器字段類。
默認是serializers.ChoiceField
調用下面的方法來確定應該自動包含在序列化器類中每個字段的類和關鍵字參數(shù)。這些方法都應該返回兩個 (field_class, field_kwargs)元祖。
調用后生成對應標準模型字段的序列化器字段。
默認實現(xiàn)是根據(jù)serializer_field_mapping屬性返回一個序列化器類。
調用后生成對應關聯(lián)模型字段的序列化器字段。
默認實現(xiàn)是根據(jù)serializer_relational_field屬性返回一個序列化器類。
這里的relation_info參數(shù)是一個命名元祖,包含model_field,related_model,to_many和has_through_model屬性。
當depth選項被設置時,被調用后生成一個對應到關聯(liá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屬性。
被調用后生成一個對應到模型類中屬性或無參數(shù)方法的序列化器字段。
默認實現(xiàn)是返回一個ReadOnlyField類。
被調用后為序列化器自己的url字段生成一個序列化器字段。默認實現(xiàn)是返回一個HyperlinkedIdentityField類。
當字段名稱沒有對應到任何模型字段或者模型屬性時調用。 默認實現(xiàn)會拋出一個錯誤,盡管子類可能會自定義該行為。
HyperlinkedModelSerializer類類似于ModelSerializer類,不同之處在于它使用超鏈接來表示關聯(lián)關系而不是主鍵。
默認情況下序列化器將包含一個url字段而不是主鍵字段。
url字段將使用HyperlinkedIdentityField字段來表示,模型的任何關聯(lián)都將使用HyperlinkedRelatedField字段來表示。
你可以通過將主鍵添加到fields選項中來顯式的包含,例如:
class AccountSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Account
fields = ('url', 'id', 'account_name', 'users', 'created')
當實例化一個HyperlinkedModelSerializer時,你必須在序列化器的上下文中包含當前的request值,例如:
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關鍵字參數(shù)查找實例。
你可以通過在extra_kwargs中設置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'}
}
或者你可以顯式的設置序列化器上的字段。例如:
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配置有時可能會有點困難。打印一個HyperlinkedModelSerializer實例的repr是一個特別有用的方式來檢查關聯(lián)關系映射的那些視圖名稱和查詢字段。
URL字段的名稱默認為'url'。你可以通過使用URL_FIELD_NAME設置進行全局性修改。
ListSerializer類能夠序列化和一次驗證多個對象。你通常不需要直接使用ListSerializer,而是應該在實例化一個序列化器時簡單地傳遞一個many=True參數(shù)。
當一個序列化器在帶有many=True選項被序列化時,將創(chuàng)建一個ListSerializer實例。該序列化器類將成為ListSerializer類的子類。
下面的參數(shù)也可以傳遞給ListSerializer字段或者一個帶有many=True參數(shù)的序列化器。
默認是True,但是如果你不想把空列表當作有效輸入的話可以把它設置成False。
下面是你可能希望要定制ListSerializer行為的一些情況。例如:
對于這些情況,當你可以通過使用序列化器類的Meta類下面的list_serializer_class選項來修改當many=True時正在使用的類。
例如:
class CustomListSerializer(serializers.ListSerializer):
...
class CustomSerializer(serializers.Serializer):
...
class Meta:
list_serializer_class = CustomListSerializer
多個對象的創(chuàng)建默認實現(xiàn)是簡單地調用列表中每個對象的.create()方法。如果要自定義實現(xiàn),那么你需要自定義當被傳遞many=True參數(shù)時使用的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類不支持多對象的更新。這是因為插入和刪除的預期行為是不明確的。
要支持多對象更新的話你需要自己明確地實現(xiàn)。編寫多個對象更新的代碼時要注意以下幾點:
你需要向實例序列化器中顯式添加一個id字段。默認隱式生成的id字段是read_only。這就導致它在更新時被刪除。一旦你明確地聲明它,它將在列表序列化器的update方法中可用。
下面是一個你可以選擇用來做多個對象更新的示例:
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):
# 我們需要使用主鍵來識別列表中的元素,
# 所以在這里使用可寫的字段,而不是默認的只讀字段。
id = serializers.IntegerField()
...
id = serializers.IntegerField(required=False)
class Meta:
list_serializer_class = BookListSerializer
類似于REST framework 2中allow_add_remove的自動支持多個對象更新操作可能會在3.1版本的第三方包中提供。
當帶有many=True參數(shù)的序列化器被實例化時,我們需要確定哪些參數(shù)和關鍵字參數(shù)應該被傳遞給子類Serializer和父類ListSerializer的.__init__()方法。
默認實現(xiàn)是將所有參數(shù)都傳遞給兩個類,出了validators和任何關鍵字參數(shù)。這兩個參數(shù)都假定用于子序列化器類。
偶爾,你可能需要明確指定當被傳遞many=True參數(shù)時,子類和父類應該如何實例化。你可以使用many_init類方法來執(zhí)行此操作。
@classmethod
def many_init(cls, *args, **kwargs):
# 實例化子序列化器類。
kwargs['child'] = cls()
# 實例化列表序列化父類
return CustomListSerializer(*args, **kwargs)
BaseSerializer 可以很簡單的用來替代序列化和反序列化的樣式。
該類實現(xiàn)與Serializer類相同的基本API:
它還有可以覆寫的四種方法,具體取決于你想要序列化類支持的功能:
因為此類提供與Serializer類相同的接口,所以你可以將它與現(xiàn)有的基于類的通用視圖一起使用,就像使用常規(guī)Serializer或ModelSerializer一樣。
這樣做時你需要注意到的唯一區(qū)別是BaseSerializer類并不會在可瀏覽的API頁面中生成HTML表單。
要使用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實例轉換為原始數(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)
要創(chuàng)建一個讀寫都支持的序列化器,我們首先需要實現(xiàn).to_internal_value()方法。這個方法返回用來構造對象實例的經(jīng)過驗證的值,如果提供的數(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')
# 執(zhí)行數(shù)據(jù)有效性校驗
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.'
})
# 返回通過驗證的數(shù)據(jù) 這用來作為 `.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類還可以用來創(chuàng)建新的通用序列化程序基類來處理特定的序列化樣式或者用來整合備用存儲后端。
下面這個類是一個可以將任意對象強制轉換為基本表示的通用序列化程序的示例。
class ObjectSerializer(serializers.BaseSerializer):
"""
一個只讀序列化程序,它將任意復雜的對象強制轉換為內置數(shù)據(jù)類型表示。
"""
def to_representation(self, obj):
for attribute_name in dir(obj):
attribute = getattr(obj, attribute_name)
if attribute_name('_'):
# 忽略私有屬性
pass
elif hasattr(attribute, '__call__'):
# 忽略方法和其他可調用對象
pass
elif isinstance(attribute, (str, int, bool, float, type(None))):
# 內置的原始數(shù)據(jù)類型不做修改
output[attribute_name] = attribute
elif isinstance(attribute, list):
# 遞歸處理列表中的對象
output[attribute_name] = [
self.to_representation(item) for item in attribute
]
elif isinstance(attribute, dict):
# 遞歸處理字典中的對象
output[attribute_name] = {
str(key): self.to_representation(value)
for key, value in attribute.items()
}
else:
# 將其他數(shù)據(jù)類型強制轉換為字符串表示
output[attribute_name] = str(attribute)
更多建議: