drf—— 序列化组件

----->序列化器-Serializer

1、序列化组件介绍

#做用:
    1. 序列化,序列化器会把模型对象转换成字典,通过response之后变成json字符串
        -Book模型--序列化器--->字典--经过drf:Response--》json格式字符串--->传给前端
    2. 反序列化,把客户端发送过来的数据,通过request之后变成字典,序列化器能够把字典转成模型
        json格式数据---经过drf:Request-->字典---序列化器---》Book模型
    3. 反序列化,完成数据校验功能

2、序列化组件简单使用

# 序列化的使用
    -写一个序列化类继承serializers.Serializer
    -在类中写要序列化的字段
    -在视图类中,实例化获得一个序列化类的对象,把要序列化的数据传入
        ser=BookSerializer(instance=res,many=True)
    -获得字典
        ser.data就是序列化后的字典

3、序列化组件使用代码实现

'''
5个接口 1. 查询全部 Book--》get 2. 查询单个 BookDetali--》get 3. 新增一个 Book--》post 4. 删除一个 BookDetali--》delete 5. 修改一个 BookDetali--》put '''

1.查询全部

url:127.0.0.1.8000/books/  # 注意最后面的 /必定要写

 

 models.py  # 创建好模型,进行数据迁移,在数据库中手动添加至少2条数据前端

from django.db import models

class Book(models.Model):
    id = models.AutoField(primary_key=True)
    title = models.CharField(max_length=32,null=True) #测试read_only
    price = models.DecimalField(max_digits=5, decimal_places=2)
    publish = models.CharField(max_length=32)

urls.py   #配置好路由python

path('books/', views.Book.as_view()),

views.py  #写视图类git

from rest_framework.views import APIView
from rest_framework.response import Response
from app01 import models
from app01.serializer import BookSerializer

class Book(APIView):
    def get(self, request, *args, **kwargs):
        res = models.Book.objects.all()
        # 借助序列化器
        # 若是是多条,就是many=True
        # 若是是单个对象,就不写
        ser = BookSerializer(instance=res, many=True)
        print(type(ser))  # rest_framework.serializers.ListSerializer
        # 经过序列化器获得的字典
        # ser.data
        print(ser.data)
        return Response(ser.data)

serializer.py  #在app中建立存放序列化器的 类的文件 serializer.py 并写BookSerializer类数据库

# 序列化器类(序列化Book表)
# from rest_framework.serializers import Serializer
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from app01 import models


class BookSerializer(serializers.Serializer):
    # 要序列化哪一个字段
    id = serializers.IntegerField(required=False)
    # id=serializers.CharField()
    title = serializers.CharField(max_length=32,min_length=2,read_only=True)
    price = serializers.DecimalField(max_digits=5, decimal_places=2)
    # 序列化的时候看不到,由于write_only=True
   publish = serializers.CharField(max_length=32,write_only=True)

2.查询单个

url:127.0.0.1.8000/books/1 #后面的数字表明查询id为几的数据 

urls.py中加django

re_path('^books/(?P<id>\d+)', views.BookDetail.as_view()),

views.py中加json

class BookDetail(APIView):
    def get(self, request, id):
        res = models.Book.objects.all().filter(id=id).first()
        # 单个,去掉many=True
        # 加many=True和不加,ser不是同一个对象,查单个是BookSerializer对象,查多个是ListSerializer对象,这是由源码中元类决定的
        ser = BookSerializer(instance=res)
        print(type(ser))  # 打印出 app01.serializer.BookSerializer
        return Response(ser.data)

3.新增一个  ---这里涉及到保存!!!

ps:新增必须写create方法app

urls.py 不变源码分析

views.pypost

class Book(APIView):
    def get(self, request, *args, **kwargs):
        ...

    def post(self, request):
        # post提交的数据都在request.data 是个字典
        print(request.data)
        ser = BookSerializer(data=request.data)
        if ser.is_valid():  # 校验数据是否合法
            ser.save()  # 保存到数据库中
            return Response(ser.data)
        else:
            # 没有校验经过的错误信息
            return Response(ser.errors)
serializer.py
# 若是序列化类继承的是Serializer,必须重写create方法

class BookSerializer(serializers.Serializer):
    ...
    def create(self, validated_data):
        # 为何要重写create?为了跟views.py里面的Book表创建关系
        res=models.Book.objects.create(**validated_data)
        print(res)
        return res

4.修改

ps:必须写update方法测试

urls.py不变

views.py

class BookDetail(APIView):
    def get(self, request, id):
        ...

    def put(self, request, id):
        # 经过id取到对象
        res = {'code': 100, 'msg': ''}
        try:
            book = models.Book.objects.get(id=id)
            ser = BookSerializer(instance=book, data=request.data)
            ser.is_valid(raise_exception=True)
            ser.save()
            res['msg'] = '修改为功'
            res['result'] = ser.data

        except Exception as e:
            res['code'] = 101
            res['msg'] = str(e)

        return Response(res)
serializer.py
class BookSerializer(serializers.Serializer):
    ...

    def update(self, book, validated_data):
        book.title=validated_data.get('title')
        book.price=validated_data.get('price')
        book.publish=validated_data.get('publish')
        book.save()
        return book

5.删除

views.py

class BookDetail(APIView):
    ...

    def delete(self,request,id):
        response = {'code': 100, 'msg': '删除成功'}
        models.Book.objects.filter(id=id).delete()
        return Response(response)

 

4、序列化类字段类型和字段参数

# 经常使用字段类型
    -IntegerField
    -CharField
    -DecimalField
    -DateTimeField
    -。。。跟models中大差不差
    
# 经常使用字段参数
    -选项参数
        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    包含错误编号与错误信息的字典

---------------------序列化器-------------------------

5、序列化器的(反序列化之)保存功能

同三中3.增长一个 里面的保存

6、序列化器的(反序列化之)字段校验功能

 validators校验,局部钩子,全局钩子

ps:validators 列表里面能够传多个值进行校验

# 三种方式
    -字段本身的校验规则(max_length...)
    -validators的校验
        publish = serializers.CharField(max_length=32,validators=[check,])

        def check(data):
        if len(data)>10:
            raise ValidationError('最长不能超过10')
        else:
            return data
    -局部和全局钩子
        # 局部钩子,validate_字段名,须要带一个data,data就是该字段的数据
    def validate_title(self, data):
        if data.startswith('sb'):
            raise ValidationError('不能以sb开头')
        else:
            return data
    # 全局钩子
    def validate(self, attrs):
        title=attrs.get('title')
        publish=attrs.get('publish')
        if title==publish:
            raise ValidationError('书名不能跟出版社同名')
        else:
            return attrs

7、序列化类经常使用字段参数之read_only和write_only

   read_only    代表该字段仅用于序列化输出,默认False
    write_only    代表该字段仅用于反序列化输入,默认False
    
    
    class BookSerializer(serializers.Serializer):
        # 要序列化哪一个字段
        id = serializers.IntegerField(required=False)
        # id=serializers.CharField()
        title = serializers.CharField(max_length=32,min_length=2,read_only=True)
        price = serializers.DecimalField(max_digits=5, decimal_places=2)
        # 序列化的时候看不到
        publish = serializers.CharField(max_length=32,validators=[check,],write_only=True)

 8、(反序列化之高级用法之source

总结:

1用法一: 修改返回到前端的字段名
    # 若source=title 那么字段名就不能再叫title,这里叫了name
    name = serializers.CharField(max_length=32,min_length=2,source='title')
2用法二: 若是表模型中有方法
class Book(models.Model):
    ...
    def test(self):
        # python是强类型语言,不支持字符串和数字直接相加
        return self.title+str(self.price)
    # 执行表模型中的test方法,而且把返回值赋值给xxx
    xxx=serializers.CharField(source='test')
3用法三: source支持跨表操做
    addr=serializers.CharField(source='publish.addr')
    
# 能够去看源码,内部如何实现的

代码:

urls.py

models.py

from django.db import models

class Book(models.Model):
    id = models.AutoField(primary_key=True)
    title = models.CharField(max_length=32,null=True) #测试read_only
    price = models.DecimalField(max_digits=5, decimal_places=2)
    # publish = models.CharField(max_length=32)
    #修改后
    publish = models.ForeignKey(to='Publish',null=True,on_delete=models.CASCADE)
    #这是在测试source第一种方法时加的
    def test(self):
        # python是强类型语言,不支持字符串和数字直接相加
        return self.title+str(self.price)

#新建一张publish表
class Publish(models.Model):
    name=models.CharField(max_length=32)
    addr=models.CharField(max_length=32)
    #重写__str__
    def __str__(self):
        return self.name

views.py

class BookSerializer(serializers.Serializer):
    id = serializers.IntegerField(required=False)
#方法一
    name = serializers.CharField(max_length=32,min_length=2,source='title')
    price = serializers.DecimalField(max_digits=5, decimal_places=2)
#方法三,要直接显示出publish的名字 ,或 在models.py中重写__str__   
publish = serializers.CharField(max_length=32,source='publish.name')
#方法二
    xxx=serializers.CharField(source='test')
#方法三:跨表
    publish_addr=serializers.CharField(source='publish.addr')

9、模型类序列化器

1 原来用的Serilizer跟表模型没有直接联系, 模型类序列化器ModelSerilizer,跟表模型有对应关系

2 使用
    class BookModelSerializer(serializers.ModelSerializer):
        class Meta:
            model=表模型    # 跟哪一个表模型创建关系
            fields=[字段,字段] # 序列化的字段,反序列化的字段
            fields='__all__' # 全部字段都序列化,反序列化
            exclude=[字段,字段] # 排除哪些字段(不能跟fields同时使用)
            read_only_fields=['price','publish']  # 序列化显示的字段
            write_only_fields=['title']           # 反序列化须要传入的字段
            extra_kwargs ={'title':{'max_length':32,'write_only':True}}
            depth=1  # 了解,跨表1查询,最多建议写3
        # 重写某些字段
        publish = serializers.CharField(max_length=32,source='publish.name')
        # 局部钩子,全局钩子,跟原来彻底同样
3 新增,修改
    -通通不用重写create和update方法了,在ModelSerializer中重写了create和update

10、(反序列化之)高级用法之SerializerMethodField

 

#方式一:Serializer
class BookSerializer(serializers.Serializer):
    id = serializers.IntegerField(required=False)
    name = serializers.CharField(max_length=32,min_length=2,source='title')
    price = serializers.DecimalField(max_digits=5, decimal_places=2)
    publish = serializers.SerializerMethodField()
    def get_publish(self,obj):
        dic={'name':obj.publish.name,'addr':obj.publish.addr}
        return dic

#方式二:ModelSerializer
class BookModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Book
        fields = '__all__'
    publish = serializers.SerializerMethodField()
    def get_publish(self,obj):
        dic={'name':obj.publish.name,'addr':obj.publish.addr}
        return dic
    
    
  
# 方式三:使用序列化类的嵌套
class PublishSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Publish
        # fields = '__all__'
        fields = ['name','addr']


class BookModelSerializer(serializers.ModelSerializer):
    publish = PublishSerializer()

    class Meta:
        model = models.Book
        fields = '__all__'

11、序列化组件源码分析

#序列化组件,先调用__new__方法,若是many=True,生成ListSerializer对象,若是为False,生成Serializer对象
#序列化对象.data方法--调用父类data方法---调用对象本身的to_representation(自定义的序列化类无此方法,去父类找)
#Aerializer类里有to_representation方法,for循环执行attribute = field.get_attribute(instance)
#再去Field类里去找get_attribute方法,self.source_attrs就是被切分的source,而后执行get_attribute方法,source_attrs
#当参数传过去,判断是方法就加括号执行,是属性就把值取出来