REST framework 序列化組件之Serializer

 Json序列化與Serializer序列化的區別

Json:

序列化: 將對象序列化成字符串用於傳輸
反序列化: 將字符串反序列化成對象用於使用

Serializer:

序列化: 將Model類對象序列化成字符串用於傳輸
反序列化: 將字符串反序列化成Model對象用於使用

Serializer

序列化組件的工作方式與django froms組件非常相似,爲每一個model類通過一套序列化工具類

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屬性獲取。

注意:

  1. 使用序列化器的時候一定要注意,序列化器聲明瞭以後,不會自動執行,需要我們在視圖中進行調用纔可以。
  2. 序列化器無法直接接收數據,需要我們在視圖中創建序列化器對象時把使用的數據傳遞過來。
  3. 序列化器的字段聲明類似於我們前面使用過的表單系統。
  4. 開發restful api時,序列化器會幫我們把模型數據轉換成字典.
  5. drf提供的視圖會幫我們把字典轉換成json,或者把客戶端發送過來的數據轉換字典.

使用

序列化器的使用分兩個階段:

在客戶端請求時,使用序列化器可以完成對數據的反序列化。

在服務器響應時,使用序列化器可以完成對數據的序列化。

  • models.py
class User(models.Model):
    SEX_CHOICES = [
        [0, '男'],
        [1, '女'],
    ]
    name = models.CharField(max_length=64)
    pwd = models.CharField(max_length=32)
    phone = models.CharField(max_length=11, null=True, default=None)
    sex = models.IntegerField(choices=SEX_CHOICES, default=0)
    icon = models.ImageField(upload_to='icon', default='icon/default.jpg')
​
    class Meta:
        # 自定義創建的表名
        db_table = 'user'
        # admin界面中顯示的表面與表名複數形式
        verbose_name = '用戶'
        verbose_name_plural = verbose_name
​
    def __str__(self):
        return self.name
  • settings.py
# 註冊rest_framework
INSTALLED_APPS = [
    # ...
    'rest_framework',
]
​
# 配置數據庫
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'db',
        'USER': 'root',
        'PASSWORD': 'root'
    }
}
​
# media資源
MEDIA_URL = '/media/'  # 後期高級序列化類與視圖類,會使用該配置
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')  # media資源路徑
​
# 國際化配置
LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai'
USE_I18N = True
USE_L10N = True
USE_TZ = False
  • 主路由
from django.conf.urls import url, include
from django.contrib import admin
from django.views.static import serve
from django.conf import settings
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    # 路由分發
    url(r'^api/', include('api.urls')),
    # 暴露靜態文件
    url('^media/(?P<path>.*)', serve, {'document_root':settings.MEDIA_ROOT})
]
  • 子路由
urlpatterns = [
    url(r'^users/$', views.User.as_view()),
    url(r'^users/(?P<pk>.*)/$', views.User.as_view()),
]

Serializer 序列化的使用

序列化提供給前臺的字段個數由後臺決定,可以少提供

但是要提供與數據庫對應的字段,名字一定要與數據庫字段相同

可自定義序列化字段,字段名可以隨意,但要定義 def get_字段名(self, obj)  函數,來完成一些需要處理在返回的數據

  • 應用下創建serializers.py
from rest_framework import serializers, exceptions
from django.conf import settings
​
from . import models
​
class UserSerializer(serializers.Serializer):
    # 定義需要序列化的字段 名字一定要與數據庫字段相同
    name = serializers.CharField()
    phone = serializers.CharField()
    
    # 自定義序列化字段
    # 屬性名隨意,值由固定的命名規範方法提供:
    # get_屬性名(self, 參與序列化的model對象)
    # 返回值就是自定義序列化屬性的值
    gender = serializers.SerializerMethodField()
    def get_gender(self, obj):  # obj是對應表中的一條記錄(即:對象)
        # choice類型的解釋型值 get_字段_display() 來訪問
        return obj.get_sex_display()
​
    icon = serializers.SerializerMethodField()
    def get_icon(self, obj):
        # settings.MEDIA_URL: 自己配置的 /media/,給後面高級序列化與視圖類準備的
        # obj.icon不能直接作爲數據返回,因爲內容雖然是字符串,但是類型是ImageFieldFile類型
        return '%s%s%s' % (r'http://127.0.0.1:8000', settings.MEDIA_URL, str(obj.icon))
  • views.py

1)從數據庫中將要序列化給前臺的model對象,或是多個model對象查詢出來

user_obj = models.User.objects.get(pk=pk) 
# 或者
user_obj_list = models.User.objects.all()  # 得到的是查詢集QuerySet

2)將對象交給序列化處理,產生序列化對象,如果序列化的是多個數據(查詢集QuerySet),要設置many=True

user_ser = serializers.UserSerializer(user_obj) 或者
user_ser = serializers.UserSerializer(user_obj_list, many=True)
# 注意: many會遍歷user_obj_list,觸發是一個具體對象,否則就需要制定many=True

3)序列化 對象.data 就是可以返回給前臺的序列化數據

return Response({
        'status': 0,
        'msg': 0,
        'results': user_ser.data
    })

 

class User(APIView):
    def get(self, request, *args, **kwargs):
        pk = kwargs.get('pk')
        if pk:
            try:
                # 用戶對象不能直接作爲數據返回給前臺
                user_obj = models.User.objects.get(pk=pk)
                # 序列化一下用戶對象
                user_ser = serializers.UserSerializer(user_obj)
                # print(user_ser)
                # print(user_ser.data)  # {'name': 'xiongda', 'phone': '110'} 序列化後的字段
                return Response({
                    'status': 0,
                    'msg': 0,
                    'results': user_ser.data
                })
            except:
                return Response({
                    'status': 2,
                    'msg': '用戶不存在',
                })
        else:
            # 用戶對象列表(queryset)不能直接作爲數據返回給前臺
            user_obj_list = models.User.objects.all()
            # 序列化一下用戶對象
            user_ser_data = serializers.UserSerializer(user_obj_list, many=True).data
            return Response({
                'status': 0,
                'msg': 0,
                'results': user_ser_data
            })

Serializer 反序列化的使用

使用序列化器進行反序列化時,需要對數據進行驗證後,才能獲取驗證成功的數據

在定義序列化器時,指明每個字段的序列化類型和選項參數,本身就是一種驗證行爲。

1)設置必填與選填序列化字段,設置校驗規則
2)爲需要額外校驗的字段提供局部鉤子函數,如果該字段不入庫,且不參與全局鉤子校驗,可以將值取出校驗
3)爲有聯合關係的字段們提供全局鉤子函數,如果某些字段不入庫,可以將值取出校驗
4)重寫create方法,完成校驗通過的數據入庫工作,得到新增的對象

  • serializers.py
# 1) 哪些字段必須反序列化
# 2) 字段都有哪些安全校驗
# 3) 哪些字段需要額外提供校驗
# 4) 哪些字段間存在聯合校驗
# 注:反序列化字段都是用來入庫的,不會出現自定義方法屬性,會出現可以設置校驗規則的自定義屬性(如:re_pwd)

class UserDeserializer(serializers.Serializer):
    name = serializers.CharField(
        max_length=64,
        min_length=3,
        error_messages={
            'max_length': '太長',
            'min_length': '太短'
        }
    )
    pwd = serializers.CharField()
    phone = serializers.CharField(required=False)  # required = False 該字段可以不傳值
    sex = serializers.IntegerField(required=False)
​
    # 自定義有校驗規則的反序列化字段
    re_pwd = serializers.CharField(required=True)
​
    # 其中:
    # name,pwd,re_pwd爲必填字段
    # phone,sex爲選填字段
    # 五個字段都必須提供完成的校驗規則
​
​
    # 局部鉤子:validate_要校驗的字段名(self, 當前要校驗字段的值)
    # 校驗規則:校驗通過返回原值,校驗失敗,拋出異常
    def validate_name(self, value):
        print(value)  # name字段的值
        if 'g' in value.lower():  # 名字中不能出現g
            raise exceptions.ValidationError('名字非法,是個雞賊!')
        return value
​
    # 全局鉤子:validate(self, 系統與局部鉤子校驗通過的所有數據)
    # 校驗規則:校驗通過返回原值,校驗失敗,拋出異常
    def validate(self, attrs):
        print(attrs)  # OrderedDict([('name', 'haha'), ('pwd', '123'), ('phone', '120'), ('re_pwd', '123')])  可以當做是字典
        pwd = attrs.get('pwd')
        re_pwd = attrs.pop('re_pwd')
        if pwd != re_pwd:
            raise exceptions.ValidationError({'pwd&re_pwd': '兩次密碼不一致'})
        return attrs
​
    # 要完成新增,需要自己重寫 create 方法(類似接口類的規則)
    def create(self, validated_data):
        print(validated_data)  # {'name': 'haha', 'pwd': '123', 'phone': '120'}
        # 儘量在所有校驗規則完畢之後,數據可以直接入庫
        return models.User.objects.create(**validated_data)
  • views.py

1) 前端傳來的數據字段必須賦值給系列化器的data參數

book_ser = serializers.UserDeserializer(data=request_data)

2) 在獲取反序列化的數據前,必須調用is_valid()方法進行驗證,驗證成功返回True,否則返回False。

book_ser.is_valid()  # 結果爲 通過(True) | 不通過(Fales)

若檢驗不通過,可以通過序列化器對象的errors屬性獲取錯誤信息,返回字典,包含了字段和字段的錯誤

book_ser.errors

若校驗通過,可以通過序列化器對象的validated_data屬性獲取數據。

book_ser.calidated_data

3) 得到新增的對象,再正常返回

book_ser.save()
class User(APIView):
    # 只考慮單增
    def post(self, request, *args, **kwargs):
        # 請求數據
        request_data = request.data
        # 數據是否合法(增加對象需要一個字典數據)
        if not isinstance(request_data, dict) or request_data == {}:
            return Response({
                'status': 1,
                'msg': '數據有誤',
            })
        # 數據類型合法,但數據內容不一定合法,需要校驗數據,校驗(參與反序列化)的數據需要賦值給data
        book_ser = serializers.UserDeserializer(data=request_data)
​
        # 序列化對象調用is_valid()完成校驗,校驗失敗的失敗信息都會被存儲在 序列化對象.errors
        if book_ser.is_valid():
            # 校驗通過,完成新增
            book_obj = book_ser.save()  # 自動調用系列化器裏的create方法,並拿到新增的對象
            return Response({
                'status': 0,
                'msg': 'ok',
                'results': serializers.UserSerializer(book_obj).data  # 拿到序列化數據給前臺
            })
        else:
            # 校驗失敗
            return Response({
                'status': 1,
                'msg': book_ser.errors,  # 將錯誤字段的信息返回給前臺
            })

反序列化-保存數據

前面的驗證數據成功後,我們可以使用序列化器來完成數據反序列化的過程.這個過程可以把數據轉成模型類對象.

可以通過實現create()和update()兩個方法來實現。

class UserDeserializer(serializers.Serializer):
    ...

    def create(self, validated_data):  # 新增
        return models.User.objects.create(**validated_data)

    def update(self, instance, validated_data):  # 更新
        # instance爲要更新的對象實例
        instance.name = validated_data.get('name', instance.name)
        instance.pwd= validated_data.get('pwd', instance.pwd)
        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  # '倚天劍'

附加說明

1) 在對序列化器進行save()保存時,可以額外傳遞數據,這些數據可以在create()和update()中的validated_data參數獲取到

# request.user 是django中記錄當前登錄用戶的模型對象
serializer.save(xionger=request.user)

2)默認序列化器必須傳遞所有required的字段,否則會拋出驗證異常。但是我們可以使用partial參數來允許部分字段更新

# Update `comment` with partial data
serializer = CommentSerializer(comment, data={'content': u'foo bar'}, partial=True)

補充

常用字段類型

字段 字段構造方式
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頁面時,顯示的字段幫助提示信息
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章