序列化器 (Serializers)


擴展序列化器的有用性是我們想要解決的問題。然而,這不是一個微不足道的問題,它將需要一些嚴肅的設計工作。—— Russell Keith-Magee, Django 用戶組

序列化器允許將復雜數據 (如查詢集和模型實例) 轉換為可以輕松渲染成 JSONXML 或其他內容類型的原生 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) 支持自動可寫嵌套表示。

在序列化器中保存多個相關實例的另一種方法是編寫處理創建正確實例的自定義模型管理器類。

例如,假設我們希望確保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_nameusers 和 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_fieldrelated_modelto_many 和 has_through_model屬性。

.build_nested_field(self, field_name, relation_info, nested_depth)

當設置了 depth 選項時,調用以生成映射到關系模型字段的序列化器字段。

默認實現基於 ModelSerializer 或 HyperlinkedModelSerializer 動態創建嵌套的序列化器類。

nested_depth 的值是 depth 選項的值減 1。

relation_info 參數是一個命名元祖,包含 model_fieldrelated_modelto_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 BaseSerializerclasses)

使用 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 BaseSerializerclasses)

要創建讀寫序列化器,我們首先需要實現 .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 包提供可寫的嵌套模型序列化器,允許使用嵌套的相關數據創建/更新


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM