序列化器-Serializer
1定義序列化器
Django REST framework中的Serializer使用類來定義,須繼承自rest_framework.serializers.Serializer。
例如,我們已有了一個數據庫模型類BookInfo
class BookInfo(models.Model):
btitle = models.CharField(max_length=20, verbose_name='名稱')
bpub_date = models.DateField(verbose_name='發布日期', null=True)
bread = models.IntegerField(default=0, verbose_name='閱讀量')
bcomment = models.IntegerField(default=0, verbose_name='評論量')
image = models.ImageField(upload_to='booktest', verbose_name='圖片', null=True)
我們想為這個模型類提供一個序列化器,可以定義如下:
class BookInfoSerializer(serializers.Serializer):
"""圖書數據序列化器"""
id = serializers.IntegerField(label='ID', read_only=True)
btitle = serializers.CharField(label='名稱', max_length=20)
bpub_date = serializers.DateField(label='發布日期', required=False)
bread = serializers.IntegerField(label='閱讀量', required=False)
bcomment = serializers.IntegerField(label='評論量', required=False)
image = serializers.ImageField(label='圖片', required=False)
注意:serializer不是只能為數據庫模型類定義,也可以為非數據庫模型類的數據定義。serializer是獨立於數據庫之外的存在。
常用字段類型:
字段 | 字段構造方式 |
---|---|
BooleanField | BooleanField() |
NullBooleanField | NullBooleanField() |
CharField | CharField(max_length=None, min_length=None, allow_blank=False, trim_whitespace=True) |
EmailField | EmailField(max_length=None, min_length=None, allow_blank=False) |
RegexField | RegexField(regex, max_length=None, min_length=None, allow_blank=False) |
SlugField | SlugField(max_length=50, min_length=None, allow_blank=False) 正則字段,驗證正則模式 [a-zA-Z0-9*-]+ |
URLField | URLField(max_length=200, min_length=None, allow_blank=False) |
UUIDField | UUIDField(format='hex_verbose') format: 1) 'hex_verbose' 如"5ce0e9a5-5ffa-654b-cee0-1238041fb31a" 2) 'hex' 如 "5ce0e9a55ffa654bcee01238041fb31a" 3)'int' - 如: "123456789012312313134124512351145145114" 4)'urn' 如: "urn:uuid:5ce0e9a5-5ffa-654b-cee0-1238041fb31a" |
IPAddressField | IPAddressField(protocol='both', unpack_ipv4=False, **options) |
IntegerField | IntegerField(max_value=None, min_value=None) |
FloatField | FloatField(max_value=None, min_value=None) |
DecimalField | DecimalField(max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None) max_digits: 最多位數 decimal_palces: 小數點位置 |
DateTimeField | DateTimeField(format=api_settings.DATETIME_FORMAT, input_formats=None) |
DateField | DateField(format=api_settings.DATE_FORMAT, input_formats=None) |
TimeField | TimeField(format=api_settings.TIME_FORMAT, input_formats=None) |
DurationField | DurationField() |
ChoiceField | ChoiceField(choices) choices與Django的用法相同 |
MultipleChoiceField | MultipleChoiceField(choices) |
FileField | FileField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL) |
ImageField | ImageField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL) |
ListField | ListField(child=, min_length=None, max_length=None) |
DictField | DictField(child=) |
選項參數:
參數名稱 | 作用 |
---|---|
max_length | 最大長度 |
min_lenght | 最小長度 |
allow_blank | 是否允許為空 |
trim_whitespace | 是否截斷空白字符 |
max_value | 最小值 |
min_value | 最大值 |
通用參數:
參數名稱 | 說明 |
---|---|
read_only | 表明該字段僅用於序列化輸出,默認False |
write_only | 表明該字段僅用於反序列化輸入,默認False |
required | 表明該字段在反序列化時必須輸入,默認True |
default | 反序列化時使用的默認值 |
allow_null | 表明該字段是否允許傳入None,默認False |
validators | 該字段使用的驗證器 |
error_messages | 包含錯誤編號與錯誤信息的字典 |
label | 用於HTML展示API頁面時,顯示的字段名稱 |
help_text | 用於HTML展示API頁面時,顯示的字段幫助提示信息 |
2、創建Serializer對象
定義好Serializer類后,就可以創建Serializer對象了。
Serializer的構造方法為:
Serializer(instance=None, data=empty, **kwarg)
說明:
1)用於序列化時,將模型類對象傳入instance參數
2)用於反序列化時,將要被反序列化的數據傳入data參數
3)除了instance和data參數外,在構造Serializer對象時,還可通過context參數額外添加數據,如
serializer = AccountSerializer(account, context={'request': request})
通過context參數附加的數據,可以通過Serializer對象的context屬性獲取。
- 使用序列化器的時候一定要注意,序列化器聲明了以后,不會自動執行,需要我們在視圖中進行調用才可以。
- 序列化器無法直接接收數據,需要我們在視圖中創建序列化器對象時把使用的數據傳遞過來。
- 序列化器的字段聲明類似於我們前面使用過的表單系統。
- 開發restful api時,序列化器會幫我們把模型數據轉換成字典.
- drf提供的視圖會幫我們把字典轉換成json,或者把客戶端發送過來的數據轉換字典.
3、序列化器的使用
序列化器的使用分兩個階段:
- 在客戶端請求時,使用序列化器可以完成對數據的反序列化。
- 在服務器響應時,使用序列化器可以完成對數據的序列化。
序列化
基本使用
1) 先查詢出一個圖書對象
from booktest.models import BookInfo
book = BookInfo.objects.get(id=2)
2) 構造序列化器對象
from booktest.serializers import BookInfoSerializer
serializer = BookInfoSerializer(book)
3)獲取序列化數據
通過data屬性可以獲取序列化后的數據
serializer.data
# {'id': 2, 'btitle': '天龍八部', 'bpub_date': '1986-07-24', 'bread': 36, 'bcomment': 40, 'image': None}
4)如果要被序列化的是包含多條數據的查詢集QuerySet,可以通過添加many=True參數補充說明
book_qs = BookInfo.objects.all()
serializer = BookInfoSerializer(book_qs, many=True)
serializer.data
# [OrderedDict([('id', 2), ('btitle', '天龍八部'), ('bpub_date', '1986-07-24'), ('bread', 36), ('bcomment', 40), ('image', N]), OrderedDict([('id', 3), ('btitle', '笑傲江湖'), ('bpub_date', '1995-12-24'), ('bread', 20), ('bcomment', 80), ('image'ne)]), OrderedDict([('id', 4), ('btitle', '雪山飛狐'), ('bpub_date', '1987-11-11'), ('bread', 58), ('bcomment', 24), ('ima None)]), OrderedDict([('id', 5), ('btitle', '西游記'), ('bpub_date', '1988-01-01'), ('bread', 10), ('bcomment', 10), ('im', 'booktest/xiyouji.png')])]
反序列化
數據驗證
使用序列化器進行反序列化時,需要對數據進行驗證后,才能獲取驗證成功的數據或保存成模型類對象。
在獲取反序列化的數據前,必須調用is_valid()方法進行驗證,驗證成功返回True,否則返回False。
驗證失敗,可以通過序列化器對象的errors屬性獲取錯誤信息,返回字典,包含了字段和字段的錯誤。如果是非字段錯誤,可以通過修改REST framework配置中的NON_FIELD_ERRORS_KEY來控制錯誤字典中的鍵名。
驗證成功,可以通過序列化器對象的validated_data屬性獲取數據。
在定義序列化器時,指明每個字段的序列化類型和選項參數,本身就是一種驗證行為。
如我們前面定義過的BookInfoSerializer
class BookInfoSerializer(serializers.Serializer):
"""圖書數據序列化器"""
id = serializers.IntegerField(label='ID', read_only=True)
btitle = serializers.CharField(label='名稱', max_length=20)
bpub_date = serializers.DateField(label='發布日期', required=False)
bread = serializers.IntegerField(label='閱讀量', required=False)
bcomment = serializers.IntegerField(label='評論量', required=False)
image = serializers.ImageField(label='圖片', required=False)
通過構造序列化器對象,並將要反序列化的數據傳遞給data構造參數,進而進行驗證
from booktest.serializers import BookInfoSerializer
data = {'bpub_date': 123}
serializer = BookInfoSerializer(data=data)
serializer.is_valid() # 返回False
serializer.errors
# {'btitle': [ErrorDetail(string='This field is required.', code='required')], 'bpub_date': [ErrorDetail(string='Date has wrong format. Use one of these formats instead: YYYY[-MM[-DD]].', code='invalid')]}
serializer.validated_data # {}
data = {'btitle': 'python'}
serializer = BookInfoSerializer(data=data)
serializer.is_valid() # True
serializer.errors # {}
serializer.validated_data # OrderedDict([('btitle', 'python')])
is_valid()方法還可以在驗證失敗時拋出異常serializers.ValidationError,可以通過傳遞raise_exception=True參數開啟,REST framework接收到此異常,會向前端返回HTTP 400 Bad Request響應。
# Return a 400 response if the data was invalid.
serializer.is_valid(raise_exception=True)
如果覺得這些還不夠,需要再補充定義驗證行為,可以使用以下三種方法:
1) validate_字段名
對<field_name>
字段進行驗證,如
class BookInfoSerializer(serializers.Serializer):
"""圖書數據序列化器"""
...
def validate_btitle(self, value):
if 'django' not in value.lower():
raise serializers.ValidationError("圖書不是關於Django的")
return value
測試
from booktest.serializers import BookInfoSerializer
data = {'btitle': 'python'}
serializer = BookInfoSerializer(data=data)
serializer.is_valid() # False
serializer.errors
# {'btitle': [ErrorDetail(string='圖書不是關於Django的', code='invalid')]}
2) validate
在序列化器中需要同時對多個字段進行比較驗證時,可以定義validate方法來驗證,如
class BookInfoSerializer(serializers.Serializer):
"""圖書數據序列化器"""
...
def validate(self, attrs):
bread = attrs['bread']
bcomment = attrs['bcomment']
if bread < bcomment:
raise serializers.ValidationError('閱讀量小於評論量')
return attrs
測試
from booktest.serializers import BookInfoSerializer
data = {'btitle': 'about django', 'bread': 10, 'bcomment': 20}
s = BookInfoSerializer(data=data)
s.is_valid() # False
s.errors
# {'non_field_errors': [ErrorDetail(string='閱讀量小於評論量', code='invalid')]}
3) validators
在字段中添加validators選項參數,也可以補充驗證行為,如
def about_django(value):
if 'django' not in value.lower():
raise serializers.ValidationError("圖書不是關於Django的")
class BookInfoSerializer(serializers.Serializer):
"""圖書數據序列化器"""
id = serializers.IntegerField(label='ID', read_only=True)
btitle = serializers.CharField(label='名稱', max_length=20, validators=[about_django])
bpub_date = serializers.DateField(label='發布日期', required=False)
bread = serializers.IntegerField(label='閱讀量', required=False)
bcomment = serializers.IntegerField(label='評論量', required=False)
image = serializers.ImageField(label='圖片', required=False)
測試:
from booktest.serializers import BookInfoSerializer
data = {'btitle': 'python'}
serializer = BookInfoSerializer(data=data)
serializer.is_valid() # False
serializer.errors
# {'btitle': [ErrorDetail(string='圖書不是關於Django的', code='invalid')]}
反序列化-保存數據
前面的驗證數據成功后,我們可以使用序列化器來完成數據反序列化的過程.這個過程可以把數據轉成模型類對象.
可以通過實現create()和update()兩個方法來實現。
class BookInfoSerializer(serializers.Serializer):
"""圖書數據序列化器"""
...
def create(self, validated_data):
"""新建"""
return BookInfo(**validated_data)
def update(self, instance, validated_data):
"""更新,instance為要更新的對象實例"""
instance.btitle = validated_data.get('btitle', instance.btitle)
instance.bpub_date = validated_data.get('bpub_date', instance.bpub_date)
instance.bread = validated_data.get('bread', instance.bread)
instance.bcomment = validated_data.get('bcomment', instance.bcomment)
return instance
如果需要在返回數據對象的時候,也將數據保存到數據庫中,則可以進行如下修改
class BookInfoSerializer(serializers.Serializer):
"""圖書數據序列化器"""
...
def create(self, validated_data):
"""新建"""
return BookInfo.objects.create(**validated_data)
def update(self, instance, validated_data):
"""更新,instance為要更新的對象實例"""
instance.btitle = validated_data.get('btitle', instance.btitle)
instance.bpub_date = validated_data.get('bpub_date', instance.bpub_date)
instance.bread = validated_data.get('bread', instance.bread)
instance.bcomment = validated_data.get('bcomment', instance.bcomment)
instance.save()
return instance
實現了上述兩個方法后,在反序列化數據的時候,就可以通過save()方法返回一個數據對象實例了
book = serializer.save()
如果創建序列化器對象的時候,沒有傳遞instance實例,則調用save()方法的時候,create()被調用,相反,如果傳遞了instance實例,則調用save()方法的時候,update()被調用。
from db.serializers import BookInfoSerializer
data = {'btitle': '封神演義'}
serializer = BookInfoSerializer(data=data)
serializer.is_valid() # True
serializer.save() # <BookInfo: 封神演義>
from db.models import BookInfo
book = BookInfo.objects.get(id=2)
data = {'btitle': '倚天劍'}
serializer = BookInfoSerializer(book, data=data)
serializer.is_valid() # True
serializer.save() # <BookInfo: 倚天劍>
book.btitle # '倚天劍'
4、附加說明
1) 在對序列化器進行save()保存時,可以額外傳遞數據,這些數據可以在create()和update()中的validated_data參數獲取到
# request.user 是django中記錄當前登錄用戶的模型對象
serializer.save(owner=request.user)
2)默認序列化器必須傳遞所有required的字段,否則會拋出驗證異常。但是我們可以使用partial參數來允許部分字段更新
# Update `comment` with partial data
serializer = CommentSerializer(comment, data={'content': u'foo bar'}, partial=True)