擴展序列化器的有用性是我們想要解決的問題。然而,這不是一個微不足道的問題,它將需要一些嚴肅的設計工作。—— Russell Keith-Magee, Django 用戶組
序列化器允許將復雜數據 (如查詢集和模型實例) 轉換為可以輕松渲染成 JSON
,XML
或其他內容類型的原生 Python 數據類型。序列化器還提供反序列化,在驗證傳入的數據之后允許解析數據轉換回復雜類型。
REST framework 中的序列化器與 Django 的 Form
和 ModelForm
類非常相似。我們提供了一個 Serializer
類,它為您提供了強大的、通用的方法來控制響應的輸出,以及一個 ModelSerializer
類,它為創建用於處理模型實例和查詢集的序列化器提供了有用的快捷實現方式。
聲明序列化器 (Declaring Serializers)
讓我們從創建一個我們可以用於示例目的的簡單對象開始:
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
對象相應的數據。
聲明序列化器看起來與聲明表單非常相似:
from rest_framework import serializers class CommentSerializer(serializers.Serializer): email = serializers.EmailField() content = serializers.CharField(max_length=200) created = serializers.DateTimeField()
序列化對象 (Serializing objects)
我們現在可以使用 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 原生的數據類型。為了完成序列化過程,我們將數據渲染為 json
。
from rest_framework.renderers import JSONRenderer json = JSONRenderer().render(serializer.data) json # b'{"email":"leila@example.com","content":"foo bar","created":"2016-01-27T15:17:10.375877"}'
反序列化對象 (Deserializing objects)
反序列化是類似的。首先我們將一個流解析為 Python 原生的數據類型...
from django.utils.six import BytesIO from rest_framework.parsers import JSONParser stream = BytesIO(json) data = JSONParser().parse(stream)
...然后我們將這些原生數據類型恢復為驗證數據的字典。
serializer = CommentSerializer(data=data) serializer.is_valid() # True serializer.validated_data # {'content': 'foo bar', 'email': 'leila@example.com', 'created': datetime.datetime(2012, 08, 22, 16, 20, 09, 822243)}
保存實例 (Saving instances)
如果我們希望能夠返回基於驗證數據的完整對象實例,我們需要實現 .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 模型,您還需要確保這些方法將對象保存到數據庫。例如,如果 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
現在,在反序列化數據時,根據驗證的數據我們可以調用 .save()
返回一個對象實例。
comment = serializer.save()
調用 .save()
方法將創建新實例或者更新現有實例,具體取決於實例化序列化器類時是否傳遞了現有實例:
# .save() will create a new instance.
serializer = CommentSerializer(data=data) # .save() will update the existing `comment` instance. serializer = CommentSerializer(comment, data=data)
.create()
和 .update()
方法都是可選的。你可以根據你序列化器類的用例不實現、或實現其中一個或都實現。
將附加屬性傳遞給 .save()
(Passing additional attributes to .save()
)
有時您會希望您的視圖代碼能夠在保存實例時注入額外的數據。此額外數據可能包括當前用戶,當前時間或不是請求數據一部分的其他信息。
您可以通過在調用 .save()
時包含其他關鍵字參數來執行此操作。例如:
serializer.save(owner=request.user)
在 .create()
或 .update()
被調用時,任何其他關鍵字參數將被包含在 validated_data
參數中。
直接重寫 .save()
(Overriding .save()
directly.)
在某些情況下,.create()
和 .update()
方法名稱可能沒有意義。例如在 contact form 中,我們可能不會創建新的實例,而是發送電子郵件或其他消息。
在這些情況下,您可以選擇直接重寫 .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)
請注意,在上面的情況下,我們現在必須直接訪問序列化器 .validated_data
屬性。
驗證 (Validation)
在反序列化數據時,在嘗試訪問經過驗證的數據或保存對象實例之前,總是需要調用 is_valid()
。如果發生任何驗證錯誤,.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
鍵也可能存在,並將列出任何常規驗證錯誤。可以使用 REST framework 設置中的 NON_FIELD_ERRORS_KEY
來自定義 non_field_errors
鍵的名稱。
當反序列化項目列表時,錯誤將作為表示每個反序列化項目的字典列表返回。
引發無效數據的異常 (Raising an exception on invalid data)
.is_valid()
方法使用可選的 raise_exception
標志,如果存在驗證錯誤,將會拋出 serializers.ValidationError
異常。
這些異常由 REST framework 提供的默認異常處理程序自動處理,默認情況下將返回 HTTP 400 Bad Request
響應。
# Return a 400 response if the data was invalid.
serializer.is_valid(raise_exception=True)
字段級別驗證 (Field-level validation)
您可以通過向您的 Serializer
子類中添加 .validate_<field_name>
方法來指定自定義字段級的驗證。這些類似於 Django 表單中的 .clean_<field_name>
方法。
這些方法采用單個參數,即需要驗證的字段值。
您的 validate_<field_name>
方法應該返回已驗證的值或者拋出 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
,那么如果不包含該字段,則此驗證步驟不會發生。
對象級別驗證 (Object-level validation)
要執行需要訪問多個字段的任何其他驗證,請添加名為 .validate()
的方法到您的 Serializer
子類中。此方法采用單個參數,該參數是字段值的字典。如果需要,它應該拋出 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
驗證器 (Validators)
序列化器上的各個字段都可以包含驗證器,通過在字段實例上聲明,例如:
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]) ...
序列化器類還可以包括應用於完整字段數據集的可重用驗證器。通過在內部 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'] )
有關更多信息,請參閱驗證器文檔。
訪問初始數據和實例 (Accessing the initial data and instance)
將初始化對象或者查詢集傳遞給序列化器實例時,該對象將以 .instance
的形式提供。如果沒有傳遞初始化對象,那么 .instance
屬性將是 None
。
將數據傳遞給序列化器實例時,未修改的數據將以 .initial_data
的形式提供。如果 data 關鍵字參數未被傳遞,那么 .initial_data
屬性將不存在。
部分更新 (Partial updates)
默認情況下,序列化器必須傳遞所有必填字段的值,否則就會引發驗證錯誤。您可以使用 partial
參數以允許部分更新。
# Update `comment` with partial data
serializer = CommentSerializer(comment, data={'content': u'foo bar'}, partial=True)
處理嵌套對象 (Dealing with nested objects)
前面的示例適用於處理只有簡單數據類型的對象,但有時我們還需要能夠代表更復雜的物體,其中對象的某些屬性可能不是字符串、日期、整數這樣簡單的數據類型。
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) # 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()
可寫的嵌套表示 (Writable nested representations)
處理支持反序列化數據的嵌套表示時,嵌套對象的任何錯誤都將嵌套在嵌套對象的字段名稱下。
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
屬性將包含嵌套數據結構。
為嵌套表示編寫 .create()
方法 (Writing .create()
methods for nested representations)
如果您支持可寫的嵌套表示,則需要編寫處理保存多個對象的 .create()
或 .update()
方法。
下面的示例演示了如何處理嵌套的配置文件對象創建用戶。
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()
方法 (Writing .update()
methods for nested representations)
對於更新,您需要仔細考慮如何處理關系更新。例如,如果關系的數據為 None
或未提供,則應發生以下哪種情況?
- 在數據庫中將關系設置為
NULL
。 - 刪除關聯的實例。
- 忽略數據並保留這個實例。
- 拋出驗證錯誤。
這是我們之前 UserSerializer
類中 update()
方法的示例。
def update(self, instance, validated_data): profile_data = validated_data.pop('profile') # 除非應用程序正確地強制始終設置該字段,否則就應該拋出一個需要處理的`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
因為嵌套創建和更新的行為可能不明確,並且可能需要相關模型之間的復雜依賴關系,REST framework 3 要求你始終顯式的編寫這些方法。默認的 ModelSerializer
.create()
和 .update()
方法不包括對可寫嵌套表示的支持。
但是,可用的第三方軟件包 (如 DRF Writable Nested) 支持自動可寫嵌套表示。
處理在模型管理類中保存關聯實例 (Handling saving related instances in model manager classes)
在序列化器中保存多個相關實例的另一種方法是編寫處理創建正確實例的自定義模型管理器類。
例如,假設我們希望確保User實例和Profile實例始終作為一對一起創建。我們可能會編寫一個類似於下面的自定義管理器類:
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
此管理器類現在更好地封裝了 user 實例和 profile 實例始終同時創建。現在可以重寫我們在序列化程序類上的 .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'] )
有關此方法的更多詳細信息,請參閱模型管理器上的 Django 文檔,以及使用模型和管理器類的相關博客。
處理多個對象 (Dealing with multiple objects)
Serializer
類還可以處理序列化或反序列化對象列表。
序列化多個對象 (Serializing multiple objects)
要序列化查詢集或對象列表而不是單個對象實例,應在實例化序列化器時傳遞 many = True
標志。然后,您可以傳遞要序列化的查詢集或對象列表。
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'} # ]
反序列化多個對象 (Deserializing multiple objects)
反序列化多個對象的默認行為是支持多個對象創建,但不支持多個對象更新。有關如何支持或自定義這些情況的更多信息,請參閱下面的 ListSerializer 文檔。
包括額外的上下文 (Including extra context)
在某些情況下,除了要序列化的對象之外,還需要為序列化器提供額外的上下文。一種常見的情況是,如果您正在使用包含超鏈接關系的序列化器,這需要序列化器能夠訪問當前的請求以便正確生成完全限定的 URL。
您可以通過在實例化序列化器時傳遞 context
參數來提供任意的附加上下文。例如:
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
類提供了一個快捷方式,可以自動創建具有與模型字段對應的字段的 Serializer
類。
ModelSerializer
類與常規 Serializer
類相同,不同之處在於:
- 它將根據模型自動為您生成一組字段。
- 它將自動為序列化器生成驗證器,例如 unique_together 驗證器。
- 它包含默認簡單實現的
.create()
和.update()
方法。
聲明 ModelSerializer
如下所示:
class AccountSerializer(serializers.ModelSerializer): class Meta: model = Account fields = ('id', 'account_name', 'users', 'created')
默認情況下,類上的所有模型字段都將映射到相應的序列化器字段。
模型上的任何關系如 (外鍵) 都將映射到 PrimaryKeyRelatedField
。默認情況下不包括反向關系,除非在序列化關系文檔中明確包含指定。
檢查 ModelSerializer
(Inspecting a ModelSerializer
)
序列化器類生成有用的詳細表示字符串,允許您全面檢查其字段的狀態。在使用 ModelSerializer
時特別有用,因為您想確定為您自動創建了哪些字段和驗證器。
為此,使用 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())
指定要包含的字段 (Specifying which fields to include)
如果您只想在模型序列化器中使用默認字段的子集,則可以使用 fields
或 exclude
選項,就像使用 ModelForm
一樣。強烈建議您使用 fields
屬性顯式設置應序列化的所有字段。這將使得在模型更改時不太可能導致無意中暴露數據。
舉個栗子:
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
選項中的名稱可以映射到不包含模型類中存在的參數的屬性或方法。
從版本 3.3.0 開始,必須提供其中一個屬性 fields
或 exclude
。
指定嵌套序列化 (Specifying nested serialization)
默認的 ModelSerializer
使用主鍵進行關聯,但您也可以使用 depth
選項輕松的生成嵌套關聯:
class AccountSerializer(serializers.ModelSerializer): class Meta: model = Account fields = ('id', 'account_name', 'users', 'created') depth = 1
depth
選項應設置為一個整數值,該值指示在恢復為平面表示之前應該遍歷的關系深度。(后半句沒看懂!!!)
如果要自定義序列化的方式,則需要自己定義字段。
顯式指定字段 (Specifying fields explicitly)
您可以向 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
額外的字段可以對應模型上任何屬性或可調用的 (字段)。
指定只讀字段 (Specifying read only fields)
您可能希望將多個字段指定為只讀。您可以使用快捷的 Meta 選項 read_only_fields
,而不是使用 read_only=True
屬性顯式的添加每個字段。
該選項應該是字段名稱的列表或元組,並聲明如下:
class AccountSerializer(serializers.ModelSerializer): class Meta: model = Account fields = ('id', 'account_name', 'users', 'created') read_only_fields = ('account_name',)
模型中已經設置 editable=False
的字段和默認就被設置為只讀的 AutoField
字段都不需要添加到 read_only_fields
選項中。
注意:有一種特殊情況,其中只讀字段是模型級別 unique_together
約束的一部分。在這種情況下,序列化器類需要該字段來驗證約束,但也不能由用戶編輯。
處理該問題的正確方法是在序列化器上顯式指定該字段,同時提供 read_only=True
和 default=…
關鍵字參數。
其中一個例子是與當前已認證 User
的只讀關系,它與另一個標識符是 unique_together
。在這種情況下,您可以聲明用戶字段,如下所示:
user = serializers.PrimaryKeyRelatedField(read_only=True, default=serializers.CurrentUserDefault())
有關 UniqueTogetherValidator 和 CurrentUserDefault 類的詳細文檔,請查閱驗證器文檔。
附加關鍵字參數 (Additional keyword arguments)
還有一個快捷方式允許您使用 extra_kwargs
選項在字段上指定任意附加關鍵字參數。與 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
關系字段 (Relational fields)
序列化模型實例時,您可以選擇多種不同的方式來表示關聯關系。ModelSerializer
的默認表示是使用相關實例的主鍵。
替代表示方式包括使用超鏈接序列化,序列化完整的嵌套表示或者使用自定義表示的序列化。
有關詳細信息,請參閱序列化器關系文檔。
自定義字段映射 (Customizing field mappings)
ModelSerializer 類還公開了一個可以重寫的 API,以便在實例化序列化器時更改如何自動確定序列化器字段。
通常,如果 ModelSerializer
默認情況下沒有生成你需要的字段,那么您應該將它們顯式地添加到類中,或者簡單地使用常規的 Serializer
類。但是在某些情況下,您可能需要創建一個新的基類,來定義如何為任意給定模型創建序列化字段。
.serializer_field_mapping
Django 模型類到 REST framework 序列化器類的映射。您可以重寫此映射以更改應該用於每個模型類的默認序列化器類。
.serializer_related_field
此屬性應是序列化器字段類,默認情況下用於關聯字段。
對於 ModelSerializer
此屬性默認是 PrimaryKeyRelatedField
。
對於 HyperlinkedModelSerializer
此屬性默認是 serializers.HyperlinkedRelatedField
。
serializer_url_field
應該用於序列化器上任何 url
字段的序列化器字段類。
默認是 serializers.HyperlinkedIdentityField
serializer_choice_field
應用於序列化器上任何選擇字段的序列化器字段類。
默認是 serializers.ChoiceField
The field_class 和 field_kwargs API
調用下面的方法來確定應該自動包含在序列化器中每個字段的類和關鍵字參數。這些方法都應該返回 (field_class, field_kwargs)
元祖。
.build_standard_field(self, field_name, model_field)
調用以生成映射到標准模型字段的序列化器字段。
默認實現返回基於 serializer_field_mapping
屬性的序列化器類。
.build_relational_field(self, field_name, relation_info)
調用以生成映射到關系模型字段的序列化器字段。
默認實現返回基於 serializer_relational_field
屬性的序列化器類。
relation_info
參數是一個命名元祖,包含 model_field
,related_model
,to_many
和 has_through_model
屬性。
.build_nested_field(self, field_name, relation_info, nested_depth)
當設置了 depth
選項時,調用以生成映射到關系模型字段的序列化器字段。
默認實現基於 ModelSerializer
或 HyperlinkedModelSerializer
動態創建嵌套的序列化器類。
nested_depth
的值是 depth
選項的值減 1。
relation_info
參數是一個命名元祖,包含 model_field
,related_model
,to_many
和 has_through_model
屬性。
.build_property_field(self, field_name, model_class)
調用以生成映射到模型類中的屬性或零參數方法的序列化器字段。
默認實現返回 ReadOnlyField
類。
.build_url_field(self, field_name, model_class)
調用為序列化器自己的 url
字段生成序列化器字段。默認實現返回 HyperlinkedIdentityField
類。
.build_unknown_field(self, field_name, model_class)
當字段名稱未映射到任何模型字段或模型屬性時調用。默認實現會引發錯誤,但子類可以自定義此行為。
HyperlinkedModelSerializer
HyperlinkedModelSerializer
類類似於 ModelSerializer
類,不同之處在於它使用超鏈接來表示關聯關系而不是主鍵。
默認情況下,序列化器將包含 url
字段而不是主鍵字段。
url 字段將使用 HyperlinkedIdentityField
序列化器字段表示,並且模型上的任何關系將使用 HyperlinkedRelatedField
序列化器字段表示。
您可以通過將主鍵添加到 fields
選項顯式包含主鍵,例如:
class AccountSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = Account fields = ('url', 'id', 'account_name', 'users', 'created')
絕對和相對 URL (Absolute and relative URLs)
當實例化 HyperlinkedModelSerializer
時,必須在序列化器上下文中包含當前 request
,例如:
serializer = AccountSerializer(queryset, context={'request': request})
這樣做將確保超鏈接可以包含適當的主機名,以便生成完全限定的 URL,例如:
http://api.example.com/accounts/1/
而不是相對 URL,例如:
/accounts/1/
如果您確實想使用相對 URL,應在序列化器上下文中顯式傳遞 {'request':None}
。
如何確定超鏈接視圖 (How hyperlinked views are determined)
需要有一種方法來確定應該將哪些視圖用於超鏈接到模型實例。
默認情況下,超鏈接預期對應於匹配樣式 {model_name}-detail
的視圖名,並通過 pk
關鍵字參數查找實例。
你可以使用在 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
是特別有用的方式來檢查關聯關系預映射到哪些視圖名稱和查詢字段。
更改 URL 字段名稱 (Changing the URL field name)
URL 字段的名稱默認為 'url'。你可以通過使用 URL_FIELD_NAME
設置 (在 settings 文件) 進行全局覆蓋。
ListSerializer
ListSerializer
類提供同時序列化和驗證多個對象的行為。您通常不需要直接使用 ListSerializer
,而應該在實例化序列化器時簡單地傳遞 many=True
。
當序列化器被實例化並且 many = True
被傳遞時,ListSerializer
實例將被創建。然后,序列化器類將成為 ListSerializer
的子類。
以下參數也可以傳遞給 ListSerializer
字段或者被傳遞 many=True
參數的序列化器。
allow_empty
默認情況下為 True
,但如果要禁止空列表作為有效輸入,則可以設置為 False
。
自定義 ListSerializer
行為 (Customizing ListSerializer
behavior)
當您可能想要自定義 ListSerializer
行為時,有一些用例。例如:
- 你想要提供列表的特定驗證,例如檢查一個元素是否與列表中的另外一個元素沖突。
- 你想要自定義多個對象的創建或更新行為。
對於這些情況,您可以通過使用序列化器 Meta
類中的 list_serializer_class
選項來修改傳遞 many=True
時使用的類。
例如:
class CustomListSerializer(serializers.ListSerializer): ... class CustomSerializer(serializers.Serializer): ... class Meta: list_serializer_class = CustomListSerializer
自定義多重創建 (Customizing multiple create)
多個對象的創建默認實現是簡單地為列表中的每個項目調用 .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
自定義多個更新 (Customizing multiple update)
默認情況下,ListSerializer
類不支持多個更新。這是因為插入和刪除預期的行為不明確。
為了支持多個更新,您需要明確地進行這樣的操作。編寫多個更新代碼時,請務必記住以下幾點:
- 如何確定為數據列表中的每個 item 更新哪個實例?
- 如何處理插入?它們是無效的,還是創建新對象?
- 如何處理刪除?它們是暗示刪除對象還是刪除關系?它們應該被忽略,還是無效?
- 如何處理排序?改變兩個項目的位置是否意味着任何狀態改變或被忽略?
你需要向實例序列化器顯式添加 id
字段。默認隱式生成的 id
字段被標記為 read_only
。這會導致它在更新時被刪除。一旦你顯式地聲明它,它將在列表序列化器的 update
方法中可用。
以下是您可以選擇實施多個更新的示例:
class BookListSerializer(serializers.ListSerializer): def update(self, instance, validated_data): # id->instance 和 id->data item 的映射。 book_mapping = {book.id: book for book in instance} data_mapping = {item['id']: item for item in validated_data} # 執行創建和更新。 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)) # 執行刪除 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() ... class Meta: list_serializer_class = BookListSerializer
第三方包可能包括在 3.1 版本中,它為多個更新操作提供一些自動支持,類似於 REST framework 2 中存在的 allow_add_remove
行為。
自定義 ListSerializer 初始化 (Customizing ListSerializer initialization)
當帶有 many=True
的序列化器被實例化時,我們需要確定哪些參數和關鍵字參數應該被傳遞給子類 Serializer
和父類 ListSerializer
的 .__init__()
方法。
默認實現是將所有參數傳遞給兩個類,除了 validators
和任何自定義關鍵字參數,這兩個參數都假定是用於子類序列化器的。
偶爾,您可能需要顯式的指定當被傳遞 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
BaseSerialAlgisher
類,可以用來方便地支持可選的序列化和反序列化樣式。
該類實現與 Serializer
類相同的基本 API:
.data
—— 返回傳出的基元表示。.is_valid()
—— 反序列化並驗證傳入的數據。.validated_data
—— 返回經過驗證的傳入數據。.errors
—— 返回在驗證期間的任何錯誤。.save()
—— 將驗證的數據保存到對象實例中。
可以重寫四種方法,這取決於你希望序列化器類支持的功能:
.to_representation()
—— 重寫它以支持序列化,用於讀取操作。.to_internal_value()
—— 重寫它以支持反序列化,以用於寫入操作。.create()
和.update()
—— 重寫其中一個或兩個以支持保存實例。
因為此類提供了與 Serializer
類相同的接口,所以您可以像使用常規的 Serializer
或 ModelSerializer
一樣使用現有的通用的基於類的視圖。
在執行此操作時您將注意到的唯一區別是 BaseSerializer
類不會在可瀏覽的 API 中生成 HTML 表單。這是因為它們返回的數據不包括允許每個字段被渲染成合適的 HTML 輸入的所有字段信息。
只讀 BaseSerializer
類(Read-only BaseSerializer
classes)
使用 BaseSerializer
類來實現只讀序列化器,我們只需要重寫 .to_representation()
方法。讓我們看一個使用簡單 Django 模型的示例:
class HighScore(models.Model): created = models.DateTimeField(auto_now_add=True) player_name = models.CharField(max_length=10) score = models.IntegerField()
創建只讀序列化器將 HighScore
實例轉換為原始數據類型很簡單。
class HighScoreSerializer(serializers.BaseSerializer): def to_representation(self, obj): return { 'score': obj.score, 'player_name': obj.player_name }
我們現在可以使用此類來序列化單個 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)
讀寫 BaseSerializer
類 (Read-write BaseSerializer
classes)
要創建讀寫序列化器,我們首先需要實現 .to_internal_value()
方法。此方法返回將用於構造對象實例的驗證值,如果提供的數據格式不正確,則可能引發 serializers.ValidationError
。
一旦實現 .to_internal_value()
,基本驗證 API 將在序列化器中可用,並且您將能夠使用 .is_valid()
,.validated_data
和 .errors
。
如果還想支持 .save()
,您還需要實現 .create()
和 .update()
方法中的一個或兩個。
這是我們之前的 HighScoreSerializer
的完整示例,它已經更新以支持讀寫操作。
class HighScoreSerializer(serializers.BaseSerializer): def to_internal_value(self, data): score = data.get('score') player_name = data.get('player_name') # 執行數據驗證。 if not score: raise serializers.ValidationError({ 'score': 'This field is required.' }) if not player_name: raise serializers.ValidationError({ 'player_name': 'This field is required.' }) if len(player_name) > 10: raise serializers.ValidationError({ 'player_name': 'May not be more than 10 characters.' }) # 返回驗證值。這將作為可用的 `.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)
創建新的基類 (Creating new base classes)
如果您想要實現新的通用序列化器類來處理特定的序列化樣式,或者集成其他存儲后端,那么 BaseSerializer
類也很有用。
以下類是可以處理將任意對象強制轉換為基本表示的通用序列化器的示例。
class ObjectSerializer(serializers.BaseSerializer): """ 任意復雜對象強制轉換為原始表示的只讀序列化器。 """ def to_representation(self, obj): for attribute_name in dir(obj): attribute = getattr(obj, attribute_name) if attribute_name('_'): # 忽略私有屬性。 pass elif hasattr(attribute, '__call__'): # 忽略方法和其他 callables。 pass elif isinstance(attribute, (str, int, bool, float, type(None))): # 原始類型可以通過未修改的方式傳遞。 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: # 將其他內容強制到其字符串表示形式。 output[attribute_name] = str(attribute)
高級序列化器用法 (Advanced serializer usage)
重寫序列化和反序列化行為 (Overriding serialization and deserialization behavior)
如果需要更改序列化器類的序列化或反序列化行為,可以通過重寫 .to_representation()
或 .to_internal_value()
方法來實現。
可能有用的一些原因包括...
- 為新的序列化器基類添加新行為。
- 對現有類稍微修改行為。
- 提高頻繁訪問返回大量數據的 API 端點的序列化性能。
這些方法的明顯特征如下:
.to_representation(self, obj)
獲取需要序列化的對象實例,並返回原始表示。通常,這意味着返回內置 Python 數據類型的結構。可以處理的確切類型取決於為 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)
將未經驗證的傳入數據作為輸入,並返回可用的已驗證數據作為 serializer.validated_data
。如果在序列化器類上調用 .save()
,則返回值也將傳遞給 .create()
或 .update()
方法。
如果任何驗證失敗,則該方法應引發 serializers.ValidationError(errors)
。errors
參數應該是將字段名稱 (或 settings.NON_FIELD_ERRORS_KEY
) 映射到錯誤消息列表的字典。如果您不需要改變反序列化行為,而是想提供對象級驗證,則建議改為重寫 .validate()
方法。
傳遞給此方法的 data
參數通常是 request.data
的值,因此它提供的數據類型將取決於為 API 配置的解析器類。
序列化器繼承 (Serializer Inheritance)
與 Django 表單類似,您可以通過繼承來擴展和重用序列化器。這允許您在父類上聲明一組通用的字段或方法,然后可以在多個序列化器中使用它們。例如,
class MyBaseSerializer(Serializer): my_field = serializers.CharField() def validate_my_field(self): ... class MySerializer(MyBaseSerializer): ...
與 Django 的 Model
和 ModelForm
類一樣,序列化器內部 Meta
類不會隱式地繼承它父類內部 Meta
類。如果您想要從父類繼承 Meta
類,則必須明確地這樣做。例如:
class AccountSerializer(MyBaseSerializer): class Meta(MyBaseSerializer.Meta): model = Account
通常我們建議不要在內部 Meta 類上使用繼承,而是顯式聲明所有選項。
此外,以下注意事項適用於序列化程序繼承:
- 正常的 Python 名稱解析規則適用。如果您有多個聲明
Meta
內部類的基類,則只使用第一個類。這意味着子類的Meta
(如果存在),否則是第一個父類的Meta
。 - 通過在子類上將名稱設置為
None
,聲明性地刪除繼承自父類的Field
是可能的。
class MyBaseSerializer(ModelSerializer): my_field = serializers.CharField() class MySerializer(MyBaseSerializer): my_field = None
但是,您只能使用這種技術選擇從父類聲明性定義的字段;它不會阻止 ModelSerializer
生成默認字段。要從默認字段中選擇,請參閱指定要包括的字段。
動態修改字段 (Dynamically modifying fields)
一旦序列化器已初始化,可以使用 .field
屬性訪問序列化器上設置的字段字典。訪問和修改此屬性允許您動態修改序列化器。
直接修改 fields
參數允許您做一些有趣的事情,例如在運行時更改序列化器字段上的參數,而不是在聲明序列化器時。
舉個栗子
例如,如果您希望能夠設置序列化器在初始化時應使用哪些字段,您可以創建一個像這樣的序列化器類:
class DynamicFieldsModelSerializer(serializers.ModelSerializer): """ ModelSerializer 獲取額外的 `fields` 參數來控制哪些字段應該被顯示。 """ def __init__(self, *args, **kwargs): # 不要將 'fields' 參數傳遞給超類 fields = kwargs.pop('fields', None) # 通常實例化超類 super(DynamicFieldsModelSerializer, self).__init__(*args, **kwargs) if fields is not None: # 刪除`fields`參數中未指定的任何字段。 allowed = set(fields) existing = set(self.fields) for field_name in existing - allowed: self.fields.pop(field_name)
這將允許您執行以下操作:
>>> 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'}
自定義默認字段 (Customizing the default fields)
REST framework 2 提供了一個 API,允許開發人員重寫如何自動生成默認字段集的 ModelSerializer
類。
該 API 包括 .get_field()
,. get_pk_field()
和其他方法。
因為版本 3.0 序列化器已經從根本上重新設計了,這個 API 不再存在。您仍然可以修改已創建的字段,但您需要參考源代碼,並且注意,如果所做的更改是針對 API 的私有位,那么它們可能會發生更改。
第三方包 (Third party packages)
以下是可用的第三方包。
Django REST marshmallow
django-rest-marshmallow 包使用 python marshmallow 庫為序列化器提供了另一種實現方式。它公開了與 REST framework 序列化器相同的 API,並且可以在某些用例中用作替代品。
Serpy
serpy 包是為速度而構建的序列化器的另一種實現。Serpy 將復雜數據類型序列化為簡單的原生類型。原生類型可以輕松轉換為 JSON 或任何其他所需格式。
MongoengineModelSerializer
django-rest-framework-mongoengine 包提供了一個支持 MongoDB 作為 Django REST framework 的存儲層的 MongoEngineModelSerializer
序列化器類。
GeoFeatureModelSerializer
django-rest-framework-gis 包提供了一個支持 GeoJSON 進行讀寫操作的 GeoFeatureModelSerializer
序列化器類。
HStoreSerializer
django-rest-framework-hstore 包提供了一個 HStoreSerializer
來支持 django-hstore DictionaryField
模型字段及其 schema-mode
特點。
Dynamic REST
dynamic-rest 包擴展了 ModelSerializer 和 ModelViewSet 接口,添加了 API 查詢參數,用於過濾,排序和包含/排除序列化器定義的所有字段和關系。
Dynamic Fields Mixin
drf-dynamic-fields 包提供了一個 mixin 來動態地限制每個序列化器的字段到 URL 參數指定的子集。
DRF FlexFields
drf-flex-fields 包擴展了 ModelSerializer 和 ModelViewSet,以提供常用的功能,用於動態設置字段並將原始字段擴展到嵌套模型,它們都來自 URL 參數和序列化器類定義。
Serializer Extensions
django-rest-framework-serializer-extensions 包提供了一系列工具來干擾序列化器,通過允許在每個視圖/請求的基礎上定義的字段。字段可以列入白名單,列入黑名單,並且可以選擇的擴展子序列化器。
HTML JSON Forms
html-json-forms 包提供了一個算法和序列化器,用於按照 (非活動) HTML JSON 表單規范處理 <form>
提交。序列化器有助於在 HTML 中處理任意嵌套的 JSON 結構。例如,<input name="items[0][id]" value="5">
將被解釋為 {"items": [{"id": "5"}]}
。
DRF-Base64
DRF-Base64 提供了一組字段和模型序列化器,用於處理 base64 編碼文件的上傳。
QueryFields
djangorestframework-queryfields 允許 API 客戶端通過包含/排除查詢參數指定將在響應中發送的字段。
DRF Writable Nested
drf-writable-nested 包提供可寫的嵌套模型序列化器,允許使用嵌套的相關數據創建/更新