DRF 之 序列化與反序列化

序列化組件

使用drf的序列化組件:

  • 1 新建一個序列化類繼承Serializer
  • 2 在類中寫要序列化的字段

在視圖中使用序列化的類:

  • 1 實例化序列化的類產生對象,在產生對象的時候,傳入需要序列化的對象(queryset)
  • 2 對象.data
  • 3 return Response(對象.data)

高級用法:

  • source:可以指定字段(name publish.name),可以指定方法。
  • SerializerMethodField搭配方法使用(get_字段名字)
  • read_only:反序列化時,不傳
  • write_only:序列化時,不顯示

序列化

首先創建表models部分:

class Book(models.Model):
    nid = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    cfn = models.IntegerField(choices=((0,'文學類'),(1,'情感類')),default=0)
    price = models.DecimalField(max_digits=5, decimal_places=2)
    publish_date = models.DateField(auto_now_add=True)

    publish = models.ForeignKey(to='Publish',to_field='nid',on_delete=models.CASCADE)
    authors = models.ManyToManyField(to='Author')

    def __str__(self):
        return self.name

class Author(models.Model):
    nid = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    age = models.IntegerField()

class Publish(models.Model):
    nid = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    city = models.CharField(max_length=32)
    email = models.EmailField()

Serializer不指定表模型

自定義 myserializers.py 文件

class AuthorSerializer(serializers.Serializer):
    name = serializers.CharField()
    age = serializers.CharField()

class BookSerializer(serializers.Serializer):
    # 指定source='name',表示序列化模型表中的name字段,重名命爲bookname(name和source='name'指定的name不能重名)
    bookname = serializers.CharField(source='name')

    # write_only 序列化的時候,該字段不顯示
    # read_only 反序列化的時候,該字段不傳
    price = serializers.CharField(write_only=True)

    # 如果要跨表取數據,比如取出版社的city,可以通過source來取值:source='publish.city'
    publish = serializers.CharField(source='publish.city')

    # source不但指定一個字段還可以指定一個函數,get_cfn_display獲取類型編號對應的中文註釋
    classification = serializers.CharField(source='get_cfn_display')

    # 序列化出版社的詳情,指定SerializerMethodField之後,可以對應一個方法返回什麼,publish_detail就是什麼內容
    publish_detail = serializers.SerializerMethodField()

    # 對應的方法固定寫法get_字段名
    def get_publish_detail(self, obj):
        return {'name': obj.publish.name, 'city': obj.publish.city, 'email': obj.publish.email}

    # 返回所有作者信息
    authors = serializers.SerializerMethodField()

    def get_authors(self, obj):
        # 利用列表推導式返回
        # return [ {'name':author.name,'age':author.age} for author in obj.authors.all()]

        # 定義一個AuthorSerializer序列化組建進行序列化
        # 這裏的obj是是一個book對象所以通過跨表查詢obj.authors.all()獲取所有的作者,因爲有多個作者,這裏要指定many=True
        authorser = AuthorSerializer(obj.authors.all(), many=True)
        return authorser.data

ModelSerializers指定了表模型

自定義 myserializers.py 文件

from rest_framework.exceptions import ValidationError
class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Book
        # 獲取指定的字段
        # fields = ('nid','name')
        # 獲取所有的字段
        fields = ('__all__')
        # 深度是1,會自動獲取所有的需要跨表的信息,1代表跨一張表,官方建議不要超過10,建議不要超過3
        # depth = 1
        # 獲取所有除了列出的字段,不能跟fields同時使用
        # exclude=['name',]
	# 重寫某個字段在Meta外部,重寫某些字段,方式同Serializers	
    classification = serializers.CharField(source='get_cfn_display')
    # 返回所有作者信息
    authors = serializers.SerializerMethodField()

對應的 view.py 文件

class Books(APIView):
    def get(self,request):
        response_dic = {'code':100,'msg':'查詢成功!'}
        books = models.Book.objects.all()
        # 如果序列化多條,many=True(也就是queryset對象,就需要寫),如果序列化一條(可以不寫),instance是要序列化的對象
        booker = BookSerializer(instance=books,many=True)
        response_dic['data'] = booker.data
        return Response(response_dic)

查詢結果

{
    "code": 100,
    "msg": "查詢成功!",
    "data": [
        {
            "nid": 1,
            "classification": "情感類",
            "authors": [
                {
                    "name": "lin",
                    "age": "12"
                },
                {
                    "name": "wow",
                    "age": "23"
                }
            ],
            "name": "紅樓夢",
            "cfn": 1,
            "price": "12.00",
            "publish_date": null,
            "publish": {
                "nid": 1,
                "name": "北京出版社",
                "city": "北京",
                "email": "[email protected]"
            }
        }
    ]
}

反序列化

繼承Serializers

使用繼承了Serializers序列化類的對象,反序列化,在自己寫的序列化類中重寫create方法。

自定義 myserializers.py 文件

class BookSerializer(serializers.Serializer):
    ... ...
    def create(self, validated_data):
        res = models.Book.objects.create(**validated_data)
        return res

對應的 view.py 文件

class Books(APIView):	
    # 使用繼承了Serializers序列化類的對象,反序列化
    def post(self,request):
        response_dic = {'code': 100, 'msg': '添加成功!'}
        # 實例化產生一個序列化類的對象,data是要反序列化的字典
        booker = BookSerializer(data=request.data)
        if booker.is_valid():
            # 清洗通過的數據,通過create方法進行保存
            res = booker.create(booker.validated_data)
        return Response(response_dic)

繼承ModelSerializers

使用繼承了ModelSerializers序列化類的對象,反序列化

自定義 myserializers.py 文件與繼承了Serializers序列化類的一樣,但是要注意定義了depth = 1在保存數據時無法外鍵的綁定。

對應的 view.py 文件

class Books(APIView):
    # 使用繼承了ModelSerializers序列化類的對象,反序列化
    def post(self, request):
        bookser = BookSerializer(data=request.data)
        # raise_exception=True調試的時候可以讓前端打印信息
        if bookser.is_valid(raise_exception=True):
            # 清洗通過的數據,通過save方法進行保存
            bookser.save()
        else:
            print(bookser.errors['name'][0])
        return Response()

反序列化的校驗

局部校驗

validate_字段名(self,value):

  • 如果校驗失敗,拋出ValidationError(拋出的異常信息需要去bookser.errors中取)
  • 如果校驗通過直接return value
class BookSerializer(serializers.Serializer):
	... ...
	def validate_naem(self,value):
        if value.startswith('sb'):
            raise ValidationError('不能以sb頭')
        return value

全局校驗

validate(self,attrs):

  • attrs所有校驗通過的數據,是個字典
  • 如果校驗失敗,拋出ValidationError
  • 如果校驗通過直接return attrs
class BookSerializer(serializers.Serializer):
	... ...
    def validate(self, attrs):
        if attrs.get('name')==attrs.grt('title'):
       		raise ValidationError('name和title不能不一致!')
        return attrs

源碼分析:

  • 全局和局部鉤子源碼部分
is_valid--->

self.run_validation(執行Serializer的run_validation)

self.to_internal_value(data)--->(執行Serializer的run_validation:485行)

全局鉤子

在這裏插入圖片描述

ModelSerializer → Serializer → run_validation
通過上面的繼承方式最後在Serializer中找的有run_validation()方法

在這裏插入圖片描述

局部鉤子

ModelSerializer → Serializer → run_validation_value
通過上面的繼承方式最後在Serializer中找的有run_validation_value()方法

  • bookser.data
- 序列化組件,先調用__new__方法,如果many=True,生成ListSerializer對象,如果爲False,生成Serializer對象
- 序列化對象.data方法 → 調用父類data方法 → 調用對象自己的to_representation(自定義的序列化類無此方法,去父類找)
- Serializer類裏有to_representation方法,for循環執行attribute = field.get_attribute(instance)
- 再去Field類裏去找get_attribute方法,self.source_attrs就是被切分的source,然後執行get_attribute方法,source_attrs當參數傳過去,判斷是方法就加括號執行,是屬性就把值取出來

在序列化的時候,傳many=True和many=False,生成的對象並不是一個對象

在這裏插入圖片描述

在這裏插入圖片描述

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章