1. 程式人生 > 實用技巧 >二、DRF框架中序列化和反序列化

二、DRF框架中序列化和反序列化

資料校驗 比如 將前端傳遞的json格式資料轉化為python中的資料型別(模型類物件)叫做反序列化 將模型類物件轉化為json格式的資料叫做序列化 資料增刪改查流程 增 校驗請求引數-->反序列化-->儲存資料-->將儲存的物件序列化並返回 刪 判斷要刪除的資料是否存在-->執行資料庫刪除並返回 改 判斷要修改的資料是否存在-->校驗請求引數-->反序列化-->儲存資料-->將儲存的物件序列化並返回 查 查詢資料庫-->將資料序列化並返回 定義序列化器注意事項 1.定義的序列化器,必須繼承rest_framework中的serializers或者Serializer子類 2.定義的每一個類屬性要與模型類中對應 3.預設情況下,定義了哪些類屬性(序列化器欄位),那麼就會序列化輸出哪些欄位和哪些欄位需要進行反序列化輸入,如果不需要序列化輸出,不定義即可,如果不要返回ID,可以不用定義ID 欄位校驗順序: 4.欄位定義時的限制(包含validators列表條目從左到右進行校驗),通過之後才會開始對單欄位進行校驗,接下來是多欄位的校驗 View中進行序列化操作
a.序列化過程:將模型類物件(或查詢集物件)轉化為json資料 b.將模型類物件(或查詢集物件)傳給instance,可以進行序列化操作 c.通過序列化器ProjectSerializer物件的data屬性,可以獲取轉化後的值 project_qs:查詢到的模型類物件 ProjectSerializer:序列化器類 many=True:如果返回多條資料(列表),需要新增這個 instance:如果需要序列化輸出,需要給instance傳參 如:serializer = serializers.ProjectSerializer(instance=project_qs, many=True) data:如果需要反序列化輸入(資料校驗),需要給data傳參 is_valid():呼叫該方法才能進行資料校驗 raise_exception=True:如果校驗通過,正常返回,如果不通過,會返回相應的錯誤資訊 serializer.validated_data:獲取校驗通過之後的資料 如: serializer = serializers.ProjectSerializer(data=project_qs, many=True) serializer.is_valid(raise_exception=True) project=Projects.objects.creat(**serializer.validated_data) 序列化器欄位釋義
label,help_text選項相當於模型類中verbose_name,help_text write_only=True,那麼當前欄位只能進行反序列化操作(資料校驗),也不會返回給前端 read_only=True,那麼當前欄位只能進行序列化操作輸出,不進行資料校驗(也就是前端可以不用傳這個值,後端仍會返回) vaildators 該欄位使用的驗證器,是一個列表,裡面可以定義內建的一些校驗規則或自己寫校驗規則,按照列表的元素順序進行校驗,不管校驗有沒有通過,都會遍歷呼叫每一個校驗器 message:表示未通過校驗返回的錯誤資訊 UniqueValidator:驗證該欄位是否有重複 如:validators=[UniqueValidator(queryset=Projects.objects.all(),message="專案名稱不能重複")] error_messages 自定義錯誤資訊的字典,key為序列化器欄位 如:error_messages={"max_length":"負責人長度不能超過50位元組","min_length":"負責人長度不能小於10位元組"} 其他序列化器欄位: max_length 最大長度 min_length 最小長度 required 是否必填,預設是 trim_whitespace 是否截斷空白字元 max_value 最大值(針對int型別) min_value 最小值(針對int型別) required 表明該欄位在反序列化時必須輸入,預設True default 反序列化時使用的預設值 allow_null 表明該欄位是否允許傳入None,預設False label 用於HTML展示API頁面時,顯示的欄位名稱 help_text 用於HTML展示API頁面時,顯示的欄位幫助提示資訊 CharField 字串型別 IntegerField int型別 序列化器類內部定義校驗器
單欄位校驗 a.方法名稱必須以validate_作為字首,後面加上待校驗的欄位名 b.在內部定義必須把value 返回return出來
def validate_name(self,value):
    """
    校驗專案名是否以“專案”結尾
    :return:
    """
    if not value.endswith('專案'):
        raise serializers.ValidationError("專案名稱必須以專案結尾")
    return value
多欄位聯合校驗 需要等單欄位校驗通過後,才會校驗多欄位,validate會把前端傳過來的引數作字典賦值給attrs
defvalidate(self, attrs): 
name
= attrs["name"] leader = attrs["leader"] if "solememory" not in name and "solememory" not in leader: raise serializers.ValidationError("solememory必須在專案負責人或者專案名稱中包含") return attrs

欄位校驗順序 validators列表條目從左到右進行校驗 ---->validate_name通過之後才會對單欄位進行校驗--->validate 再對多欄位進行校驗 序列化器類create方法
def create(self, validated_data):
    """
    更新專案
    :param validated_data: 校驗通過之後的專案資料
    :return: 專案建立成功之後的模型類物件
    """
    project = Projects.objects.create(**validated_data)
    return project

  

project_qs:查詢到的模型類物件 當在view檢視中呼叫了serializer .save()方法時,save方法可以傳遞關鍵字引數(字典型別),會被create方法中的validated_data接收 在建立序列化器物件時,(這一步的時候:serializer = serializers.ProjectSerializer(data=project_qs) 如果只給data傳參,那麼使用序列化器物件呼叫save()方法時,只會呼叫creat()方法 序列化器類update方法
# instance引數接收的是更新前的引數,validated_data接收的是更新後的引數
def update(self, instance, validated_data):
    """
  :param instance: 待更新的專案模型類物件--->接收的是one_project
  :param validated_data: 校驗通過之後的模型類物件--->接收的是python_data
  :return: 專案更新成功之後的模型類物件
    """
    instance.name = validated_data['name']
    instance.leader = validated_data['leader']
    instance.tester = validated_data['tester']
    instance.programmer = validated_data['programmer']
    instance.publish_app = validated_data['publish_app']
    instance.desc = validated_data['desc']
    instance.save()
    return instance

one_project:資料庫查詢到的模型類物件 python_data:前端傳遞過來的引數 在建立序列化器物件時,(這一步的時候:serializer = serializers.ProjectSerializer(instance=one_project, data=python_data) 如果給instance和data同時傳遞引數,當在view檢視中呼叫了serializer .save()方法時,會自動呼叫update方法 模型序列化器 因為在序列化器類中又要重新定義模型類中的欄位,而且兩邊的定義相差不大,所以可以定義Meta內部類,設定當前序列化器類的元資料資訊
class ProjectModelSerializer(serializers.ModelSerializer):
#因為模型序列化器是自動根據模型類生成的,那麼我們如果有自定義校驗器,可以這樣寫,
    這裡自定義的會覆蓋掉模型序列化器自動生成的欄位,但這裡不要輕易指定,
    一般模型類沒有的欄位,可以在這裡寫,否則最好寫在下面的extra_kwargs 中
    
name = serializers.CharField(label="專案名稱", help_text="專案名稱", max_length=20,
      validators=[is_unique_project, cotain_keyword_project])
#如果要定義模型類中沒有的欄位,可以這樣寫,但記得新增在下面的fields中
email = serializers.EmailField(label='郵箱', allow_blank=True, 
allow_null=True, default='[email protected]', read_only=True)

# 父表如果需要返回子表中的欄位,預設情況下,父表模型序列化器類不會建立子表字段,需要顯示建立
# 子表字段名為:子表類名小寫_set
# 如果子表定義外來鍵欄位時,指定了related_name='intfaces',那麼這裡需要使用related_name使用的值
interfaces_set = serializers.StringRelatedField(many=True)

class Meta:
    # 指定參照哪一個模型類
    model = Projects
    # 指定模型類中的哪些欄位來生成序列化器欄位,__all__代表所有欄位
    # 會自動將模型列中的主鍵新增read_only=True,因為id一般不需要前端傳
    # fields = '__all__'

    # 可以在fields中使用元組指定哪些模型類欄位需要生成序列化器欄位,既需要輸入也需要輸出
    # fields元組中指定的是所有序列化器欄位(模型類中不包含的欄位也需要在fields中指定)
    fields=('id', 'name', 'leader', 'tester', 'email ')

    # 排除模型類欄位,這裡面的欄位不用生成序列化器欄位
    # exclude = ('create_time', 'update_time', 'desc')

    # 在read_only_fiels中指定的欄位會自動新增read_only=True,可惜沒有write_only的方法
    # read_only_fiels= ('desc')

    # 是一個巢狀字典的字典
    # 如果只需要改欄位中的一部分內容,就沒必要重新再像上面一樣寫一個name,可以在這裡指定,以修改的欄位名作為key
    extra_kwargs = {
        'name': {
            'error_messages': {'max_length': '專案名稱的長度不能大於20位元組'},
            'read_only': True,
            'min_length': 50,
            'validators': [is_unique_project, cotain_keyword_project]
        },
        'leader': {
            'label': '負責人',
            'write_only': True
        }
    }
#ModelSerializer內部有creat和update方法,無需自己定義
# 假如email欄位設定的是write_only,那麼該欄位通過校驗之後會呼叫create方法,但是資料庫中不存在這個欄位,會導致報錯,這個時候可以重寫create方法
def create(self, validated_data):
    # 刪除emalil後再次呼叫create方法
    email = validated_data.pop('email')
    super().create(validated_data)

外來鍵表中的模型序列化器
class ProjectModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = Projects
        fields = '__all__'

class InterfacesModelSerializer(serializers.ModelSerializer):
    # 會預設將外來鍵欄位project生成PrimaryKeyRelatedField型別,預設返回的是父表對應的id值
    # 可以重寫預設生成的project,重寫改成了StringRelatedField方法,會自動呼叫父表的__str__方法,輸出父表的name
    # project = serializers.StringRelatedField(label="所屬專案")

    # 使用父表的模型序列化器來建立,就可以獲取父表中的一些欄位
    project = ProjectModelSerializer(read_only=True)

    class Meta:
        # 指定參照哪一個模型
        model = Interfaces
        fields = '__all__'