当前位置 博文首页 > drf序列化器serializer的具体使用

    drf序列化器serializer的具体使用

    作者:小杨-先生 时间:2021-08-03 17:43

    目录
    • 一、序列化器-serializer
    • 二、序列化器的使用
      • 简单使用
    • 高级使用
      • source
      • **SerializerMethodField( ) **
      • 通用参数
    • 三、反序列化数据校验
      • 字段属性
      • 局部钩子
      • 全局钩子
      • validators
    • 四、序列化器操作数据
      • 查询所有
      • 查询单条
      • 新增数据
      • 修改数据
      • 删除数据
    • 五、模型类序列化器
      • 六、源码分析many=True

        一、序列化器-serializer

        • 序列化,序列化器会把模型对象转成字典,经过response以后变成JSON字符串
        • 反序列化:把客户端发送过来的数据,经过request以后变成字典,序列化器可以把字典转成模型
        • 反序列化:完成数据校验功能

        二、序列化器的使用

        序列化器的使用分为两个阶段:

        • 在客户端请求时,使用序列化器可以完成对数据的反序列化。
        • 在服务器响应时,使用序列化器可以完成对数据的序列化。

        简单使用

        1、创建一个表模型

        from django.db import models
        
        class Books(models.Model):
            title = models.CharField(verbose_name='书名', max_length=32)
            publish = models.CharField(verbose_name='出版社', max_length=32)
            price = models.DecimalField(verbose_name='价格', max_digits=5, decimal_places=2)
        
        

        2、新建一个py文件,写一个序列化的类,继承Serializer

        3、在类中写要序列化的字段,想序列化那个字段,就在类中写那个字段

        from rest_framework import serializers
        
        class BooksSerializer(serializers.Serializer):
            title = serializers.CharField()
            publish = serializers.CharField()
            price = serializers.DecimalField()
        
        

        4、在视图类中使用,导入——》实例化得到序列化对象,把要序列化的对象传入

        5、序列化的对象.data——》是一个字典

        6、把字典返回,如果不使用rest_framework提供的Response,就得使用JsonResponse

        from rest_framework.views import APIView
        from rest_framework.request import Request
        from app01.models import Books
        from app01.ser import BooksSerializer
        
        class BookView(APIView):
            def get(self, request, pk):
                # 响应信息
                response_msg = {'status': 200, 'message': '查询成功'}
                # 获取要序列化的对象
                book = Books.objects.filter(pk=pk).first()
                # 要序列化谁就把谁传到序列化类去
                book_ser = BooksSerializer(book)
                # book_ser.data————》序列化对象.data————》就是序列化后的字典
                # 将查询结果添加到响应信息内
                response_msg['data'] = book_ser.data
                return Response(response_msg)
            
         # urls.py
        re_path(r'^book/(?P<pk>\d+)/', views.BookView.as_view()),
        
        

        7、如果要被序列化的是包含多条数据的查询集queryset,可以通过添加many=True参数

        from rest_framework.views import APIView
        from rest_framework.response import Response
        from app01.models import Books
        from app01.ser import BooksSerializer
        
        
        class BooksView(APIView):
            def get(self, request):
                # 响应信息
                response_msg = {'status': 200, 'message': '查询成功'}
                books = Books.objects.all()
                # 要序列化谁就把谁传到序列化类去
                book_ser = BooksSerializer(books, many=True)
                # book_ser.data————》序列化对象.data————》就是序列化后的字典
                # 将查询结果添加到响应信息内
                response_msg['data'] = book_ser.data
                return Response(response_msg)
        
        # urls.py
        re_path(r'^books/', views.BookView.as_view()),
        
        

        高级使用

        source

        1、可以修改字段名字

        class BooksSerializer(serializers.Serializer):
            xxx = serializers.CharField(source='title')  # 相当于——》xxx = Books.title
              
        # 响应
        {
            "status": 200,
            "message": "查询成功",
            "data": {
                "xxx": "魔道祖师"   ————》响应的字段名被修改了
            }
        }

        2、可以跨表查询

        class BookSerializer(serializers.Serializer):
            publish_email = serializers.CharField(source='publish.email')
            # 相当于——》publish_email = Book.publish.email 连表查询publish表的email字段
            
            
        # 响应
        {
            "status": 200,
            "message": "查询成功",
            "data": {
                "publish_email": "modao@163.com"
            }
        }
        

        3、可以执行方法

        # models.py
        class Book(models.Model):
            title = models.CharField(max_length=32)
            price = models.IntegerField()
            pub_date = models.DateTimeField()
            publish = models.ForeignKey("Publish", on_delete=models.CASCADE, null=True)
            authors = models.ManyToManyField("Author")
        
            def func(self):
                return '666666'
        
            
        # ser.py
        class BookSerializer(serializers.Serializer):
            msg = serializers.CharField(source='func')
            # msg = Book.func  ——》调用Book类中的func()方法的返回值
            
            
        # 响应
        {
            "status": 200,
            "message": "查询成功",
            "data": {
                "msg": "666666"
            }
        }

        **SerializerMethodField( ) **

        它需要有一个配套的方法,方法名叫做get_字段名,返回值就是要显示的东西

        class BookSerializer(serializers.Serializer):
            authors = serializers.SerializerMethodField()
        
            def get_authors(self, instance):
                # instance ——》 Book对象
                authors = instance.authors.all()    # 取出所有作者
                author_list = []
                for author in authors:
                    author_list.append({'name': author.name, 'age': author.age})
                return author_list
        
        

        通用参数

        read_only:(只读)表明该字段仅用于序列化输出,默认False,如果设置成True,响应中可以看到该字段,修改时,不需要传该字段

        write_only:(只写)表明该字段仅用于反序列化输入,默认False,如果设置成True,响应中看不到该字段,修改时,该字段需要传

        from rest_framework import serializers
        
        class BooksSerializer(serializers.Serializer):
            title = serializers.CharField(read_only = True)  # 响应中能看到改字段,修改不需要传值
            publish = serializers.CharField(write_only = True) # 响应中看不到改字段,修改需要传值
            price = serializers.DecimalField()

        还有参数如下:

        • required  表明该字段在反序列化时必须输入,默认True
        • default   反序列化时使用的默认值
        • allow_null  表明该字段是否允许传入None,默认False
        • validators  该字段使用的验证器
        • error_messages 包含错误编号与错误信息的字典

        三、反序列化数据校验

        当使用序列化器对数据进行反序列化时,就需要对数据进行校验了,只有校验成功的数据才能被保存成模型类对象

        将要校验的数据传入序列化器中并实例化:obj = BooksSerializer(data=request.data),调用is_valid()方法校验,校验成功返回True,失败返回False。

        失败,可以通过序列化器对象的errors获取错误信息(字典)

        成功,可以公共序列化对象的validated_data属性获取数据。

        校验方法有:局部钩子,全局钩子,validators,和序列化类型和字段属性也是

        字段属性

        • max_length  最大长度
        • min_lenght  最小长度
        • allow_blank  是否允许为空
        • trim_whitespace 是否截断空白字符
        • max_value  最小值
        • min_value  最大值

        局部钩子

        在序列化器类中创建局部钩子:validate_字段名,并且接收一个参数

        # ser.py
        class BooksSerializer(serializers.Serializer):
            title = serializers.CharField()
            publish = serializers.CharField()
            price = serializers.DecimalField(max_digits=5, decimal_places=2)
            
            # 局部钩子对price字段校验
            def validate_price(self, data):
                if float(data) > 20:
                    # 校验成功就通过
                    return data
                else:
                    # 校验失败就抛异常
                    raise ValidationError('价格太低')
        

        全局钩子

        全局钩子:validate( ), 接收一个参数,

        同时对多个字段进行比较验证

        # ser.py
        class BooksSerializer(serializers.Serializer):
            title = serializers.CharField()
            publish = serializers.CharField()
            price = serializers.DecimalField(max_digits=5, decimal_places=2)
            
        def validate(self, validate_data):
            title = validate_data.get('title')
            publish = validate_data.get('publish')
            if not title == publish:
                return validate_data
            else:
                raise ValidationError('书名和出版社不能一致')
        

        validators

        使用字段的validators=[func],来校验

        # ser.py
        # 校验函数
        def check_price(data):
            if float(data) > 10:
                return data
            else:
                raise ValidationError('价格太低')
        
        class BooksSerializer(serializers.Serializer):
            title = serializers.CharField()
            publish = serializers.CharField()
            price = serializers.CharField(validators=[check_price]) # 配置

        四、序列化器操作数据

        查询所有

        # views.py
        class BooksView(APIView):
            def get(self, request):
                # 响应信息
                response_msg = {'status': 200, 'message': '查询成功'}
                # 获取所有数据
                books = Books.objects.all()
                # 把数据谁传到序列化器中
                book_ser = BooksSerializer(instance=books, many=True) # 序列化多条需要加 many=True
                # book_ser.data————》序列化对象.data————》就是序列化后的字典
                # 将查询结果添加到响应信息内
                response_msg['data'] = book_ser.data
                return Response(response_msg)
            
        # urls.py
        path('books/', views.BooksView.as_view()),
        

        查询单条

        # views.py
        class BookView(APIView):
            def get(self, request, pk):
                # 响应信息
                response_msg = {'status': 200, 'message': '查询成功'}
                # 获取要序列化的对象
                book = Books.objects.filter(pk=pk).first()
                # 要序列化谁就把谁传到序列化器中
                book_ser = BooksSerializer(instance=book)
                # book_ser.data————》序列化对象.data————》就是序列化后的字典
                # 将查询结果添加到响应信息内
                response_msg['data'] = book_ser.data
                return Response(response_msg)
            
        # urls.py
        re_path(r'^book/(?P<pk>\d+)/', views.BookView.as_view()),
        

        新增数据

        新增数据需要在序列化器中重写create( ) 方法:

        注意没有传递instance实例,则调用save()方法的时候,create()被调用,相反,如果传递了instance实例,调用save()方法的时候,update()被调用。

        # views.py
        class BookView(APIView):
            def post(self, request):
                # 响应信息
                response_msg = {'status': 201, 'message': '增加成功'}
                # 修改才有instance,新增没有instance,只有data
                book_ser = BooksSerializer(data=request.data)
        
                # 校验字段
                if book_ser.is_valid():
                    book_ser.save()  # 需要在序列化器中重写create()方法
                    # 保存成功把原数据返回
                    response_msg['data'] = book_ser.data
                else:
                    response_msg['status'] = 202
                    response_msg['message'] = '数据校验失败'
                    response_msg['data'] = book_ser.error_messages
                return Response(response_msg)
            
            
        # ser.py
        class BooksSerializer(serializers.Serializer):
            title = serializers.CharField()
            publish = serializers.CharField()
            price = serializers.DecimalField(max_digits=5, decimal_places=2)
        
            # 重写create
            def create(self, validated_data):   # validated_data——>传入的新增数据
                instance = Books.objects.create(**validated_data)
                # instance——> 新增的字段对象,需要返回
                return instance
               
        # urls.py
        path('book/', views.BookView.as_view()),

        修改数据

        修改数据需要在序列化器中重写update( ) 方法:

        # views.py
        class BookView(APIView):
            def put(self, request, pk):
                # 响应信息
                response_msg = {'status': 200, 'message': '修改成功'}
                # 获取需要修改的字段对象
                book = Books.objects.filter(pk=pk).first()
                # 将字段对象和修改数据添加到序列化器中
                book_ser = BooksSerializer(instance=book, data=request.data)
        
                # 校验数据
                if book_ser.is_valid():
                    book_ser.save()     # 需要在序列化器中重写update()方法
                    response_msg['data'] = book_ser.data
                else:
                    response_msg['status'] = 202
                    response_msg['message'] = '数据校验失败'
                    response_msg['data'] = book_ser.error_messages
                return Response(response_msg)
            
        # urls.py
        re_path('book/(?P<pk>\d+)', views.BookView.as_view()),

        删除数据

        # views.py
        class BooksView(APIView):
            def delete(self, request, pk):
            # 响应信息
            response_msg = {'status': 200, 'message': '删除成功'}
            # 删除数据
            Books.objects.filter(pk=pk).delete()
            return Response(response_msg)
            
        # urls.py
        re_path('book/(?P<pk>\d+)', views.BooksView.as_view()),
        

        五、模型类序列化器

        DRF提供了ModelSerializer模型类序列化器来帮助我们快速创建一个Serializer类。

        ModelSerializer与常规的Serializer相同,但是提供了:

        • 基于模型类自动生成一系列字段
        • 基于模型类自动为Serializer生成validators,比如unique_together
        • 包含默认的create( ) 和update( )。

        实例:

        class BookSerializer(serializers.ModelSerializer):
            class Meta:
                model = Book        # 指明参照那个模型类
                fields = '__all__'  # 为模型类的那些字段生成
        

        字段操作

        1、可以使用fields来明确字段,__all__表示包含所以字段,具体那些字段->fields = ('title','price')

        2、exclude表示排除那些字段,不能和fields一起写——>exclude = ('price',)

        3、额外参数extra_kwargs,给字段添加额外的参数

        class BookSerializer(serializers.ModelSerializer):
            class Meta:
                model = Book        # 指明参照那个模型类
                fields = '__all__'  # 为模型类的那些字段生成
          
                # 类似于  title = serializers.CharField(read_only = True)
                extra_kwargs = {
                    'title': {'read_only': True},
                }
        

        六、源码分析many=True

        当我们需要查询多条数据时就需要在实例化序列化器的时候传many=True

        book_ser = BooksSerializer(instance=books, many=True) # 查询多条
        book_one_ser = BooksSerializer(instance=book) # 查询单条
        
        print(type(book_ser))
        #<class 'rest_framework.serializers.ListSerializer'>
        
        print(type(book_one_ser))
        #<class 'app01.ser.BookModelSerializer'>
        
        # 对象的生成-->先调用类的__new__方法,生成空对象,如果many=True,生成ListSerializer对象,反之生成Serializer对象
        
        # 类的__new__方法控制对象的生成
        def __new__(cls, *args, **kwargs):
            # 如果many=True,就会自动创建ListSerializer类
            if kwargs.pop('many', False):
                return cls.many_init(*args, **kwargs)
            return super().__new__(cls, *args, **kwargs)
        
        
        jsjbwy