官方原文連結
本系列文章 github 地址
轉載請註明出處
Serializer 欄位
Form 類中的每個欄位不僅負責驗證資料,還負責 “清洗” 它 — 將其規範化為一致的格式。
序列化欄位處理基本資料型別和其他資料型別(比如自定義的類)之間的轉換。它們還可以對資料進行驗證,以及從其父物件中檢索和設定值。
注意: 序列化欄位都宣告在 fields.py
中,但按照慣例,應該使用 from rest_framework import serializers
,並用 serializers.<FieldName>
的方式引用。
核心引數
每個序列化欄位類的建構函式都需要一些引數。某些欄位類需要附加特定於該欄位的引數,但應始終接受以下引數:
read_only
只讀欄位包含於輸出 API 中,不應該包含在需要建立或更新操作的輸入 API 中。在序列化類輸入中錯誤的包含 'read_only' 會被忽略。
將其設定為 True
可確保在序列化表示時使用該欄位,但在反序列化期間建立或更新例項時不使用該欄位。
預設為 False
write_only
將其設定為 True
以確保在更新或建立例項時可以使用該欄位,但在序列化表示時不包括該欄位。
預設為 False
required
如果在反序列化過程中沒有該提供欄位,通常會出現錯誤。如果在反序列化過程中不需要此欄位,則應該設定為 false。
將此設定為 False
還允許在序列化例項時從輸出中省略物件屬性或字典金鑰。如果金鑰不存在,它將不會包含在輸出表示中。
預設為 True
allow_null
如果把 None
傳遞給序列化欄位,通常會引發錯誤。如果 None
應被視為有效值,則將此關鍵字引數設定為 True
。
請注意,將此引數設定為 True
將意味著序列化輸出的預設值為 null
,但並不意味著輸入反序列化的預設值。
預設為 False
default
如果設定,則會給出預設值,在沒有提供輸入值時,將使用該預設值。如果未設定,則預設行為是不填充該屬性。
部分更新操作時不應該使用 default
。因為有些情況下,只有傳入資料中提供的欄位才會返回驗證值。
可以設定為函式或其他可呼叫的物件,在這種情況下,每次使用該值時都會對其進行呼叫。被呼叫時,它將不會收到任何引數。如果可呼叫物件具有 set_context
方法,那麼在每次將欄位例項作為引數獲取值之前都會呼叫該方法。這與驗證器的工作方式相同。
在序列化例項時,如果物件屬性或字典關鍵字不存在於例項中,將使用預設值。
請注意,設定預設值意味著該欄位不是必需的。同時包括 default
和 required
的關鍵字引數都是無效的,會引發錯誤。
source
將用於填充欄位的屬性的名稱。可以是一個只接受 self
引數的方法,如 URLField(source='get_absolute_url')
,或者使用點符號來遍歷屬性,如 EmailField(source='user.email')
。在使用點符號時,如果在屬性遍歷期間任何物件不存在或為空,則可能需要提供預設值。
source ='*'
具有特殊含義,用於表示整個物件應該傳遞到該欄位。這對建立巢狀表示或對於需要訪問完整物件以確定輸出表示的欄位非常有用。
預設為該欄位的名稱。
validators
應該應用於傳入欄位輸入的驗證函式列表,該列表中的函式應該引發驗證錯誤或僅返回。驗證器函式通常應該引發 serializers.ValidationError
,但 Django 的內建 ValidationError
也支援與 Django 程式碼庫或第三方 Django 包中定義的驗證器相容。
error_messages
一個字典,key 是錯誤程式碼, value 是對應的錯誤資訊。
label
一個簡短的文字字串,可用作 HTML 表單欄位或其他描述性元素中欄位的名稱。
help_text
一個文字字串,可用作 HTML 表單欄位或其他描述性元素中欄位的描述。
initial
應該用於預填充 HTML 表單欄位的值。你可能會傳遞一個可呼叫物件,就像你對任何常規 Django Field
所做的一樣:
import datetime
from rest_framework import serializers
class ExampleSerializer(serializers.Serializer):
day = serializers.DateField(initial=datetime.date.today)
複製程式碼
style
可用於控制渲染器渲染欄位的鍵值對的字典。
這裡有兩個例子是 'input_type'
和 'base_template'
:
# Use <input type="password"> for the input.
password = serializers.CharField(
style={'input_type': 'password'}
)
# Use a radio input instead of a select input.
color_channel = serializers.ChoiceField(
choices=['red', 'green', 'blue'],
style={'base_template': 'radio.html'}
)
複製程式碼
Boolean 欄位
BooleanField
表示一個 boolean 值。
使用 HTML 編碼表單時需要注意,省略一個 boolean 值被視為將欄位設定為 False
,即使它指定了 default=True
選項。這是因為 HTML 核取方塊通過省略該值來表示未選中的狀態,所以 REST framework 將省略看作是空的核取方塊。
請注意,將使用 required=False
選項生成預設的 BooleanField
例項(因為 Django models.BooleanField
始終為 blank=True
)。如果想要更改此行為,請在序列化類上顯式宣告 BooleanField
。
對應與 django.db.models.fields.BooleanField
.
簽名: BooleanField()
NullBooleanField
表示一個布林值,它也接受 None
作為有效值。
對應與 django.db.models.fields.NullBooleanField
.
簽名: NullBooleanField()
字串欄位
CharField
表示文字。可以使用 max_length
, min_length
驗證(或限定)文字的長短。
對應與 django.db.models.fields.CharField
或 django.db.models.fields.TextField
.
簽名: CharField(max_length=None, min_length=None, allow_blank=False, trim_whitespace=True)
max_length
- 驗證輸入所包含的字元數不超過這個數目。min_length
- 驗證輸入所包含的字元數不少於這個數目。allow_blank
- 如果設定為True
,則空字串應被視為有效值。如果設定為False
,那麼空字串被認為是無效的並會引發驗證錯誤。預設為False
。trim_whitespace
- 如果設定為True
,則前後空白將被刪除。預設為True
。
allow_null
選項也可用於字串欄位,儘管它相對於 allow_blank
來說不被推薦。同時設定 allow_blank=True
和 allow_null=True
是有效的,但這樣做意味著字串表示允許有兩種不同型別的空值,這可能導致資料不一致和微妙的應用程式錯誤。
EmailField
表示文字,將文字驗證為有效的電子郵件地址。
對應與 django.db.models.fields.EmailField
簽名: EmailField(max_length=None, min_length=None, allow_blank=False)
RegexField
表示文字,用於驗證給定的值是否與某個正規表示式匹配。
對應與 django.forms.fields.RegexField
.
簽名: RegexField(regex, max_length=None, min_length=None, allow_blank=False)
強制的 regex
引數可以是一個字串,也可以是一個編譯好的 Python 正規表示式物件。
使用 Django 的 django.core.validators.RegexValidator
進行驗證。
SlugField
一個根據模式 [a-zA-Z0-9_-]+
驗證輸入的 RegexField
。
對應與 django.db.models.fields.SlugField
.
簽名: SlugField(max_length=50, min_length=None, allow_blank=False)
URLField
一個根據 URL 匹配模式驗證輸入的 RegexField
。完全合格的 URL 格式為 http://<host>/<path>
。
對應與 django.db.models.fields.URLField
. 使用 Django 的 django.core.validators.URLValidator
進行驗證。
簽名: URLField(max_length=200, min_length=None, allow_blank=False)
UUIDField
確保輸入的欄位是有效的 UUID 字串。to_internal_value
方法將返回一個 uuid.UUID
例項。在輸出時,欄位將以規範的連字元格式返回一個字串,例如:
"de305d54-75b4-431b-adb2-eb6b9e546013"
複製程式碼
簽名: UUIDField(format='hex_verbose')
format
: 確定 uuid 值的表示形式'hex_verbose'
- 權威的十六進位制表示形式,包含連字元:"5ce0e9a5-5ffa-654b-cee0-1238041fb31a"
'hex'
- 緊湊的十六進位制表示形式, 不包含連字元:"5ce0e9a55ffa654bcee01238041fb31a"
'int'
- 128 位整數表示形式:"123456789012312313134124512351145145114"
'urn'
- RFC 4122 URN 表示形式:"urn:uuid:5ce0e9a5-5ffa-654b-cee0-1238041fb31a"
修改format
僅影響表示值。所有格式都被to_internal_value
接受。
FilePathField
一個其選項僅限於檔案系統上某個目錄中的檔名的欄位。
對應於 django.forms.fields.FilePathField
.
簽名: FilePathField(path, match=None, recursive=False, allow_files=True, allow_folders=False, required=None, **kwargs)
path
- FilePathField 應該從中選擇的目錄的絕對檔案系統路徑。match
- 用來過濾檔名的正規表示式,string 型別。recursive
- 指定是否應該包含路徑的所有子目錄。預設值是False
。allow_files
- 是否應該包含指定位置的檔案。預設值為True
。這個引數或allow_folders
必須是True
。(兩個屬性必須有一個為true
)allow_folders
- 是否應該包含指定位置的資料夾。預設值是False
。這個引數或allow_files
必須是True
。(兩個屬性必須有一個為true
)
IPAddressField
確保輸入是有效的 IPv4 或 IPv6 字串。
對應於 django.forms.fields.IPAddressField
和 django.forms.fields.GenericIPAddressField
.
簽名: IPAddressField(protocol='both', unpack_ipv4=False, **options)
protocol
將有效輸入限制為指定的協議。接受的值是'both'
(預設),'IPv4'
或'IPv6'
。匹配不區分大小寫。unpack_ipv4
解壓 IPv4 對映的地址,如::ffff:192.0.2.1
。如果啟用此選項,則該地址將解壓到 192.0.2.1。 預設是禁用的。只能在protocol
設定為'both'
時使用。
數字欄位
IntegerField
表示整數。
對應於 django.db.models.fields.IntegerField
, django.db.models.fields.SmallIntegerField
, django.db.models.fields.PositiveIntegerField
和 django.db.models.fields.PositiveSmallIntegerField
。
簽名: IntegerField(max_value=None, min_value=None)
max_value
驗證所提供的數字不大於這個值。min_value
驗證所提供的數字不小於這個值。
FloatField
表示浮點。
對應於 django.db.models.fields.FloatField
.
簽名: FloatField(max_value=None, min_value=None)
max_value
驗證所提供的數字不大於這個值。min_value
驗證所提供的數字不小於這個值。
DecimalField
表示十進位制,由 Python 用 Decimal
例項表示。
對應於 django.db.models.fields.DecimalField
.
簽名: DecimalField(max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None)
max_digits
允許的最大位數。它必須是None
或大於等於decimal_places
的整數。decimal_places
小數位數。coerce_to_string
如果應返回字串值,則設定為True
;如果應返回Decimal
物件,則設定為False
。預設值與COERCE_DECIMAL_TO_STRING
settings key 的值相同,除非被覆蓋,否則該值將為True
。如果序列化物件返回Decimal
物件,則最終的輸出格式將由渲染器決定。請注意,設定localize
將強制該值為True
。max_value
驗證所提供的數字不大於這個值。min_value
驗證所提供的數字不小於這個值。localize
設定為True
以啟用基於當前語言環境的輸入和輸出本地化。這也會迫使coerce_to_string
為True
。預設為False
。請注意,如果你在 settings 檔案中設定了USE_L10N=True
,則會啟用資料格式化。rounding
設定量化到配置精度時使用的舍入模式。 有效值是decimal
模組舍入模式。預設為None
。
用法示例
若要驗證數字到999,精確到 2 位小數,應該使用:
serializers.DecimalField(max_digits=5, decimal_places=2)
複製程式碼
用10位小數來驗證數字不超過10億:
serializers.DecimalField(max_digits=19, decimal_places=10)
複製程式碼
這個欄位還接受一個可選引數,coerce_to_string
。如果設定為 True
,則表示將以字串形式輸出。如果設定為 False
,則表示將保留為 Decimal
例項,最終表示形式將由渲染器確定。
如果未設定,則預設設定為與 COERCE_DECIMAL_TO_STRING
setting 相同的值,除非另行設定,否則該值為 True
。
日期和時間欄位
DateTimeField
表示日期和時間。
對應於 django.db.models.fields.DateTimeField
.
簽名: DateTimeField(format=api_settings.DATETIME_FORMAT, input_formats=None)
format
- 表示輸出格式的字串。如果未指定,則預設為與DATETIME_FORMAT
settings key 相同的值,除非設定,否則將為'iso-8601'
。設定為格式化字串則表明to_representation
返回值應該被強制為字串輸出。格式化字串如下所述。將此值設定為None
表示 Pythondatetime
物件應由to_representation
返回。在這種情況下,日期時間編碼將由渲染器確定。input_formats
- 表示可用於解析日期的輸入格式的字串列表。 如果未指定,則將使用DATETIME_INPUT_FORMATS
設定,該設定預設為['iso-8601']
。
DateTimeField
格式化字串。
格式化字串可以是明確指定的 Python strftime 格式,也可以是使用 ISO 8601 風格 datetime 的特殊字串 iso-8601
。(例如 '2013-01-29T12:34:56.000000Z'
)
當一個 None
值被用於格式化 datetime
物件時,to_representation
將返回,最終的輸出表示將由渲染器類決定。
auto_now
和 auto_now_add
模型欄位。
使用 ModelSerializer
或 HyperlinkedModelSerializer
時,請注意,auto_now=True
或 auto_now_add=True
的模型欄位預設情況下將使用 read_only=True
。
如果想覆蓋此行為,則需要在序列化類中明確宣告 DateTimeField
。例如:
class CommentSerializer(serializers.ModelSerializer):
created = serializers.DateTimeField()
class Meta:
model = Comment
複製程式碼
DateField
表示日期。
對應於 django.db.models.fields.DateField
簽名: DateField(format=api_settings.DATE_FORMAT, input_formats=None)
format
- 表示輸出格式的字串。如果未指定,則預設為與DATE_FORMAT
settings key 相同的值,除非設定,否則將為'iso-8601'
。設定為格式化字串則表明to_representation
返回值應該被強制為字串輸出。格式化字串如下所述。將此值設定為None
表示 Pythondate
物件應由to_representation
返回。在這種情況下,日期時間編碼將由渲染器確定。input_formats
- 表示可用於解析日期的輸入格式的字串列表。 如果未指定,則將使用DATE_INPUT_FORMATS
設定,該設定預設為['iso-8601']
。
DateField
格式化字串
格式化字串可以是明確指定的 Python strftime 格式,也可以是使用 ISO 8601 風格 date 的特殊字串 iso-8601
。(例如 '2013-01-29'
)
TimeField
表示時間。
對應於 django.db.models.fields.TimeField
簽名: TimeField(format=api_settings.TIME_FORMAT, input_formats=None)
format
- 表示輸出格式的字串。如果未指定,則預設為與TIME_FORMAT
settings key 相同的值,除非設定,否則將為'iso-8601'
。設定為格式化字串則表明to_representation
返回值應該被強制為字串輸出。格式化字串如下所述。將此值設定為None
表示 Pythontime
物件應由to_representation
返回。在這種情況下,日期時間編碼將由渲染器確定。input_formats
- 表示可用於解析日期的輸入格式的字串列表。 如果未指定,則將使用TIME_INPUT_FORMATS
設定,該設定預設為['iso-8601']
。
TimeField
格式化字串
格式化字串可以是明確指定的 Python strftime 格式,也可以是使用 ISO 8601 風格 time 的特殊字串 iso-8601
。(例如 '12:34:56.000000'
)
DurationField
表示持續時間。
對應於 django.db.models.fields.DurationField
這些欄位的 validated_data
將包含一個 datetime.timedelta
例項。該表示形式是遵循格式 '[DD] [HH:[MM:]]ss[.uuuuuu]'
的字串。
簽名: DurationField()
選擇欄位
ChoiceField
可以從一個有限的選擇中接受值的欄位。
如果相應的模型欄位包含 choices=…
引數,則由 ModelSerializer
自動生成欄位。
簽名: ChoiceField(choices)
choices
- 有效值列表,或(key, display_name)
元組列表。allow_blank
- 如果設定為True
,則空字串應被視為有效值。如果設定為False
,那麼空字串被認為是無效的並會引發驗證錯誤。預設是False
。html_cutoff
- 如果設定,這將是 HTML 選擇下拉選單中顯示的選項的最大數量。可用於確保自動生成具有非常大可以選擇的 ChoiceField,而不會阻止模板的渲染。預設是None
.html_cutoff_text
- 指定一個文字指示器,在截斷列表時顯示,比如在 HTML 選擇下拉選單中已經截斷了最大數量的專案。預設就會顯示"More than {count} items…"
Allow_blank
和 allow_null
都是 ChoiceField
上的有效選項,但強烈建議只使用一個而不是兩個都用。對於文字選擇,allow_blank
應該是首選,allow_null
應該是數字或其他非文字選項的首選。
MultipleChoiceField
可以接受一組零、一個或多個值的欄位,從有限的一組選擇中選擇。採取一個必填的引數。 to_internal_value
返回一個包含選定值的 set
。
簽名: MultipleChoiceField(choices)
choices
- 有效值列表,或(key, display_name)
元組列表。allow_blank
- 如果設定為True
,則空字串應被視為有效值。如果設定為False
,那麼空字串被認為是無效的並會引發驗證錯誤。預設是False
。html_cutoff
- 如果設定,這將是 HTML 選擇下拉選單中顯示的選項的最大數量。可用於確保自動生成具有非常大可以選擇的 ChoiceField,而不會阻止模板的渲染。預設是None
.html_cutoff_text
- 指定一個文字指示器,在截斷列表時顯示,比如在 HTML 選擇下拉選單中已經截斷了最大數量的專案。預設就會顯示"More than {count} items…"
Allow_blank
和 allow_null
都是 ChoiceField
上的有效選項,但強烈建議只使用一個而不是兩個都用。對於文字選擇,allow_blank
應該是首選,allow_null
應該是數字或其他非文字選項的首選。
檔案上傳欄位
解析和檔案上傳。
FileField
和 ImageField
類只適用於 MultiPartParser
或 FileUploadParser
。大多數解析器,例如, e.g. JSON 不支援檔案上傳。 Django 的常規 FILE_UPLOAD_HANDLERS
用於處理上傳的檔案。
FileField
表示檔案。執行 Django 的標準 FileField 驗證。
對應於 django.forms.fields.FileField
.
簽名: FileField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)
max_length
- 指定檔名的最大長度。allow_empty_file
- 指定是否允許空檔案。use_url
- 如果設定為True
,則 URL 字串值將用於輸出表示。如果設定為False
,則檔名字串值將用於輸出表示。預設為UPLOADED_FILES_USE_URL
settings key 的值,除非另有設定,否則為True
。
ImageField
表示圖片。驗證上傳的檔案內容是否匹配已知的圖片格式。
對應於 django.forms.fields.ImageField
.
簽名: ImageField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)
max_length
- 指定檔名的最大長度。allow_empty_file
- 指定是否允許空檔案。use_url
- 如果設定為True
,則 URL 字串值將用於輸出表示。如果設定為False
,則檔名字串值將用於輸出表示。預設為UPLOADED_FILES_USE_URL
settings key 的值,除非另有設定,否則為True
。
需要 Pillow
庫或 PIL
庫。 建議使用 Pillow
庫。 因為 PIL
已經不再維護。
複合欄位
ListField
驗證物件列表的欄位類。
簽名: ListField(child=<A_FIELD_INSTANCE>, min_length=None, max_length=None)
child
- 應該用於驗證列表中的物件的欄位例項。如果未提供此引數,則列表中的物件將不會被驗證。min_length
- 驗證列表中包含的元素數量不少於這個數。max_length
- 驗證列表中包含的元素數量不超過這個數。
例如,要驗證整數列表:
scores = serializers.ListField(
child=serializers.IntegerField(min_value=0, max_value=100)
)
複製程式碼
ListField
類還支援一種宣告式風格,允許編寫可重用的列表欄位類。
class StringListField(serializers.ListField):
child = serializers.CharField()
複製程式碼
我們現在可以在我們的應用程式中重新使用我們自定義的 StringListField
類,而無需為其提供 child
引數。
DictField
驗證物件字典的欄位類。DictField
中的鍵總是被假定為字串值。
簽名: DictField(child=<A_FIELD_INSTANCE>)
child
- 應該用於驗證字典中的值的欄位例項。如果未提供此引數,則對映中的值將不會被驗證。
例如,要建立一個驗證字串到字串對映的欄位,可以這樣寫:
document = DictField(child=CharField())
複製程式碼
你也可以像使用 ListField
一樣使用宣告式風格。例如:
class DocumentField(DictField):
child = CharField()
複製程式碼
JSONField
驗證傳入的資料結構由有效 JSON 基元組成的欄位類。在其二進位制模式下,它將表示並驗證 JSON 編碼的二進位制字串。
簽名: JSONField(binary)
binary
- 如果設定為True
,那麼該欄位將輸出並驗證 JSON 編碼的字串,而不是原始資料結構。預設是False
.
其他型別的欄位
ReadOnlyField
只是簡單地返回欄位的值而不進行修改的欄位類。
當包含與屬性相關的欄位名而不是模型欄位時,此欄位預設與 ModelSerializer
一起使用。
簽名: ReadOnlyField()
例如,如果 has_expired
是 Account
模型中的一個屬性,則以下序列化程式會自動將其生成為 ReadOnlyField
:
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
fields = ('id', 'account_name', 'has_expired')
複製程式碼
HiddenField
不根據使用者輸入獲取值的欄位類,而是從預設值或可呼叫值中獲取值。
簽名: HiddenField()
例如,要包含始終提供當前時間的欄位作為序列化類驗證資料的一部分,則可以使用以下內容:
modified = serializers.HiddenField(default=timezone.now)
複製程式碼
如果需要根據某些預先提供的欄位值執行某些驗證,則通常只需要 HiddenField
類,而不是將所有這些欄位公開給終端使用者。
ModelField
可以繫結到任意模型欄位的通用欄位。ModelField
類將序列化/反序列化的任務委託給其關聯的模型欄位。該欄位可用於為自定義模型欄位建立序列化欄位,而無需建立新的自定義序列化欄位。
ModelSerializer
使用此欄位來對應自定義模型欄位類。
簽名: ModelField(model_field=<Django ModelField instance>)
ModelField
類通常用於內部使用,但如果需要,可以由 API 使用。為了正確例項化 ModelField
,必須傳遞一個附加到例項化模型的欄位。例如:ModelField(model_field=MyModel()._meta.get_field('custom_field'))
SerializerMethodField
這是一個只讀欄位。它通過呼叫它所連線的序列化類的方法來獲得它的值。它可用於將任何型別的資料新增到物件的序列化表示中。
簽名: SerializerMethodField(method_name=None)
method_name
- 要呼叫序列化物件的方法的名稱。如果不包含,則預設為get_<field_name>
.
由 method_name
引數引用的序列化方法應該接受一個引數(除了 self
),這是要序列化的物件。它應該返回你想要包含在物件的序列化表示中的任何內容。例如:
from django.contrib.auth.models import User
from django.utils.timezone import now
from rest_framework import serializers
class UserSerializer(serializers.ModelSerializer):
days_since_joined = serializers.SerializerMethodField()
class Meta:
model = User
def get_days_since_joined(self, obj):
return (now() - obj.date_joined).days
複製程式碼
自定義欄位
如果你想建立自定義欄位,則需要對 Field
進行子類化,然後重寫 .to_representation()
和 .to_internal_value()
方法中的一個或兩個。這兩個方法用於在初始資料型別和基本序列化資料型別之間進行轉換。基本資料型別通常是 number,string, boolean, date
/time
/datetime
或 None
。它們也可以是任何僅包含其他基本物件的列表或字典。其他型別可能會支援,但具體取決於你使用的渲染器。
呼叫 .to_representation()
方法將初始資料型別轉換為基本的可序列化資料型別。
呼叫 to_internal_value()
方法將基本資料型別恢復為其內部 python 表示形式。如果資料無效,此方法應該引發 serializers.ValidationError
異常。
請注意,2.x 版本中存在的 WritableField
類不再存在。 應此,如果欄位需要支援資料輸入,則應該繼承 Field
並覆蓋 to_internal_value()
。
看幾個栗子
基本的自定義欄位
我們來看一個序列化代表 RGB 顏色值的類的例子:
class Color(object):
"""
A color represented in the RGB colorspace.
"""
def __init__(self, red, green, blue):
assert(red >= 0 and green >= 0 and blue >= 0)
assert(red < 256 and green < 256 and blue < 256)
self.red, self.green, self.blue = red, green, blue
class ColorField(serializers.Field):
"""
Color objects are serialized into 'rgb(#, #, #)' notation.
"""
def to_representation(self, obj):
return "rgb(%d, %d, %d)" % (obj.red, obj.green, obj.blue)
def to_internal_value(self, data):
data = data.strip('rgb(').rstrip(')')
red, green, blue = [int(col) for col in data.split(',')]
return Color(red, green, blue)
複製程式碼
預設情況下,欄位值被視為對映到物件的屬性。如果需要自定義欄位值的訪問方式,則需要覆蓋 .get_attribute()
和/或 .get_value()
。
讓我們建立一個可以用來表示被序列化物件的類名的欄位:
class ClassNameField(serializers.Field):
def get_attribute(self, obj):
# We pass the object instance onto `to_representation`,
# not just the field attribute.
return obj
def to_representation(self, obj):
"""
Serialize the object's class name.
"""
return obj.__class__.__name__
複製程式碼
丟擲驗證錯誤
我們上面的 ColorField
類目前不執行任何資料驗證。為了表示無效資料,我們應該引發一個 serializers.ValidationError
,如下所示:
def to_internal_value(self, data):
if not isinstance(data, six.text_type):
msg = 'Incorrect type. Expected a string, but got %s'
raise ValidationError(msg % type(data).__name__)
if not re.match(r'^rgb\([0-9]+,[0-9]+,[0-9]+\)$', data):
raise ValidationError('Incorrect format. Expected `rgb(#,#,#)`.')
data = data.strip('rgb(').rstrip(')')
red, green, blue = [int(col) for col in data.split(',')]
if any([col > 255 or col < 0 for col in (red, green, blue)]):
raise ValidationError('Value out of range. Must be between 0 and 255.')
return Color(red, green, blue)
複製程式碼
.fail()
方法是引發 ValidationError
的快捷方式,它從 error_messages
字典中接收訊息字串。例如:
default_error_messages = {
'incorrect_type': 'Incorrect type. Expected a string, but got {input_type}',
'incorrect_format': 'Incorrect format. Expected `rgb(#,#,#)`.',
'out_of_range': 'Value out of range. Must be between 0 and 255.'
}
def to_internal_value(self, data):
if not isinstance(data, six.text_type):
self.fail('incorrect_type', input_type=type(data).__name__)
if not re.match(r'^rgb\([0-9]+,[0-9]+,[0-9]+\)$', data):
self.fail('incorrect_format')
data = data.strip('rgb(').rstrip(')')
red, green, blue = [int(col) for col in data.split(',')]
if any([col > 255 or col < 0 for col in (red, green, blue)]):
self.fail('out_of_range')
return Color(red, green, blue)
複製程式碼
這種風格讓你的錯誤資訊更清晰,並且與程式碼分離,應該是首選。
使用 source='*'
這裡我們將舉一個具有 x_coordinate
和 y_coordinate
屬性的平面 DataPoint
模型的示例。
class DataPoint(models.Model):
label = models.CharField(max_length=50)
x_coordinate = models.SmallIntegerField()
y_coordinate = models.SmallIntegerField()
複製程式碼
使用自定義欄位和 source ='*'
,我們可以提供座標對的巢狀表示形式:
class CoordinateField(serializers.Field):
def to_representation(self, obj):
ret = {
"x": obj.x_coordinate,
"y": obj.y_coordinate
}
return ret
def to_internal_value(self, data):
ret = {
"x_coordinate": data["x"],
"y_coordinate": data["y"],
}
return ret
class DataPointSerializer(serializers.ModelSerializer):
coordinates = CoordinateField(source='*')
class Meta:
model = DataPoint
fields = ['label', 'coordinates']
複製程式碼
請注意,此示例不處理驗證。使用 source ='*'
的巢狀序列化類可以更好地處理座標巢狀,並且帶有兩個 IntegerField
例項,每個例項都有自己的 source
指向相關欄位。
然而,這個例子的關鍵點是:
to_representation
傳遞整個DataPoint
物件, 並且會對映到所需的輸出。
>>> instance = DataPoint(label='Example', x_coordinate=1, y_coordinate=2)
>>> out_serializer = DataPointSerializer(instance)
>>> out_serializer.data
ReturnDict([('label', 'Example'), ('coordinates', {'x': 1, 'y': 2})])
複製程式碼
- 除非我們的欄位是隻讀的,否則
to_internal_value
必須對映回適合更新目標物件的字典。使用source='*'
,to_internal_value
的返回將更新根驗證的資料字典,而不是單個鍵。
>>> data = {
... "label": "Second Example",
... "coordinates": {
... "x": 3,
... "y": 4,
... }
... }
>>> in_serializer = DataPointSerializer(data=data)
>>> in_serializer.is_valid()
True
>>> in_serializer.validated_data
OrderedDict([('label', 'Second Example'),
('y_coordinate', 4),
('x_coordinate', 3)])
複製程式碼
為了完整性,我們再次做同樣的事情,但是使用上面建議的巢狀序列化方法:
class NestedCoordinateSerializer(serializers.Serializer):
x = serializers.IntegerField(source='x_coordinate')
y = serializers.IntegerField(source='y_coordinate')
class DataPointSerializer(serializers.ModelSerializer):
coordinates = NestedCoordinateSerializer(source='*')
class Meta:
model = DataPoint
fields = ['label', 'coordinates']
複製程式碼
這裡,我們在 IntegerField
宣告中處理目標和源屬性對(x
和 x_coordinate
,y
和 y_coordinate
)之間的對映。這是使用了 source ='*'
的 NestedCoordinateSerializer
。
新的 DataPointSerializer
展現出與自定義欄位方法相同的行為。
序列化:
>>> out_serializer = DataPointSerializer(instance)
>>> out_serializer.data
ReturnDict([('label', 'testing'),
('coordinates', OrderedDict([('x', 1), ('y', 2)]))])
複製程式碼
反序列化:
>>> in_serializer = DataPointSerializer(data=data)
>>> in_serializer.is_valid()
True
>>> in_serializer.validated_data
OrderedDict([('label', 'still testing'),
('x_coordinate', 3),
('y_coordinate', 4)])
複製程式碼
雖然沒有編寫驗證,但是可以使用內建的驗證方式:
>>> invalid_data = {
... "label": "still testing",
... "coordinates": {
... "x": 'a',
... "y": 'b',
... }
... }
>>> invalid_serializer = DataPointSerializer(data=invalid_data)
>>> invalid_serializer.is_valid()
False
>>> invalid_serializer.errors
ReturnDict([('coordinates',
{'x': ['A valid integer is required.'],
'y': ['A valid integer is required.']})])
複製程式碼
出於這個原因,可以首先嚐試巢狀序列化類方法。當巢狀序列化類變得不可行或過於複雜時,可以使用自定義欄位方法。