• DRF中的序列化器详细应用


    视图的功能:说白了就是接收前端请求,进行数据处理

    (这里的处理包括:如果前端是GET请求,则构造查询集,将结果返回,这个过程为序列化;如果前端是POST请求,假如要对数据库进行改动,则需要拿到前端发来的数据,进行校验,将数据写入数据库,这个过程称为反序列化)

    最原始的视图可以实现这样的逻辑处理,但是针对不同的请求,需要在类视图中定义多个方法实现各自的处理,这样是可以解决问题,但是存在一个缺陷,那就是每个函数中一般的逻辑都差不多:读请求,从数据库拿数据,写东西到数据库,返回结果给前端。这样就会产生大量的重复代码。

    在开发REST API的视图中,虽然每个视图具体操作的数据不同,但增、删、改、查的实现流程基本套路化,所以这部分代码也是可以复用简化编写的:

    增:校验请求数据 -> 执行反序列化过程 -> 保存数据库 -> 将保存的对象序列化并返回

    删:判断要删除的数据是否存在 -> 执行数据库删除

    改:判断要修改的数据是否存在 -> 校验请求的数据 -> 执行反序列化过程 -> 保存数据库 -> 将保存的对象序列化并返回

    查:查询数据库 -> 将数据序列化并返回

    1. 安装DRF

    pip install djangorestframework

    2. 添加rest_framework应用

    我们利用在Django框架学习中创建的demo工程,在settings.py的INSTALLED_APPS中添加'rest_framework'。

    INSTALLED_APPS = [
        ...
        'rest_framework',
    ]

    为了节省我们写代码的时间,DRF框架为我们提供了实现视图的快捷方式。那么DRF框架的核心是什么?那就是序列化器实现序列化,反序列化以及视图

    1.序列化器
    定义序列化器(本质就是一个类),一般包括模型类的字段,有自己的字段类型规则。实现了序列化器后,就可以创建序列化对象以及查询集进行序列化操作,通过序列化对象.data来获取数据(不用自己构造字典,再返回Json数据)

    1.用于序列化时,将模型类对象传入instance参数

    2.用于反序列化时,将要被反序列化的数据传入data参数

    3.除了instance和data参数外,在构造Serializer对象时,还可通过context参数额外添加数据,如

    serializer = AccountSerializer(account, context={'request': request})

    通过context参数附加的数据,可以通过Serializer对象的context属性获取。

    一个序列化器的例子:

    class BookInfoSerializer(serializers.Serializer):
        """图书序列化器"""
        id = serializers.IntegerField(label='ID', read_only=True)
        btitle = serializers.CharField(label='名称', max_length=20)
        bpub_date = serializers.DateField(label='发布日期', required=False)
        bread = serializers.IntegerField(label='阅读量', required=False)
        bcomment = serializers.IntegerField(label='评论量', required=False)
        image = serializers.ImageField(label='图片', required=False)
     
        # 在一方关联多方:在序列化书籍信息时,同时序列化出书籍关联的任务信息
        # StringRelatedField : 也是适用的,显示汉字字段而不是指定的PrimaryKey,这样更直观
        # many=True :指定heroinfo_set是多的那一方
        # read_only:该字段只能进行序列化,反序列化时直接忽略该字段
        heroinfo_set = serializers.PrimaryKeyRelatedField(label='英雄', read_only=True, many=True)

    这样就可以通过:

    book = BookInfo.objects.get(id=1) # 获取查询集
    s = BookInfoSerializer(book) # 将查询集绑定给序列化器
    print(s.data) # 通过序列化对象.data将数据读出来

    一个反序列化例子:

    # 实现新增数据
    data = {'btitle':'钢铁是怎样炼成的'}
    s = BookInfoSerializer(data = data) # 将查询集绑定给序列化器
    serializer.is_valid(raise_exception=True)  # 进行校验
    print(s.validated_data) # 通过序列化对象.data将数据读出来
     
     
    # 实现修改数据
    data = {'btitle':'钢铁是怎样炼成的'}
    book = BookInfo.objects.get(id=1) # 获取查询集
    s = BookInfoSerializer(book, data = data) # 将查询集以及前端传过来的数据绑定给序列化器
    serializer.is_valid(raise_exception=True) # 进行校验
    print(s.validated_data) # 通过序列化对象.data将数据读出来

    raise_exception=True参数开启,REST framework接收到此异常,会向前端返回HTTP 400 Bad Request响应。

    从上边我们可以看到,反序列化要求我们进行数据的验证,默认会帮我们验证前端传来的数据是否合法。这里传入的是bititle,因为这个字段是必须传入的。is_valid()方法只能提供基础的验证,如果不能满足我们,就可以自定义新的验证。

    自定义验证的方式:

    1.给单个以及多个数据进行验证添加

    # 给某字段增加校验逻辑
    def validate_btitle(self, value): # 这里的value就是前端传来的数据,这里代表'钢铁是怎样炼成的'
        if 'fenghua' not in value.lower():
            raise serializers.ValidationError('bititle必须包含fenghua字段')
        return value
     
    # 给多个字段增加校验
    def validate(self, attrs):
        bread = attrs['bread']
        bcomment = attrs['bcomment']
        if bread < bcomment:
            raise serializers.ValidationError('阅读量小于评论量')
        return attrs

    2.给所有字段都增加验证(在序列化器外边进行函数定义,在序列化器内部字段中添加validators属性即可)

    def about_django(value):
        if 'django' not in value.lower():
            raise serializers.ValidationError("图书不是关于Django的")
     
    class BookInfoSerializer(serializers.Serializer):
        """图书数据序列化器"""
        id = serializers.IntegerField(label='ID', read_only=True)
        # 在字段中添加validators,进行外边函数的添加
        btitle = serializers.CharField(label='名称', max_length=20, validators=[about_django])
        bpub_date = serializers.DateField(label='发布日期', required=False)
        bread = serializers.IntegerField(label='阅读量', required=False)
        bcomment = serializers.IntegerField(label='评论量', required=False)
        image = serializers.ImageField(label='图片', required=False)

    那么验证之后就可以了吗?不是,还需要将验证后的数据进行保存!!

    实现:在序列化器中定义两个方法进行数据写入:

    复习:数据库增加数据

    # 方式1
    book = BookInfo(
        btitle='西游记',
        bput_date=date(1988,1,1),
        bread=10,
        bcomment=10
    )
    book.save()
     
     
    方式2:
    book.create(
        btitle='西游记',
        bput_date=date(1988,1,1),
        bread=10,
        bcomment=10
    )
    # 校验后保存数据,实现父类声明的方法
    # validated_data是经过校验之后的数据,已经是标准的字典
    def create(self, validated_data):
        # 对字典进行拆包
        return BookInfo.objects.create(**validated_data)
     
    def update(self, instance, validated_data):# instance是数据集对象
        # 取不到就取默认值
        instance.btitle = validated_data.get('btitle', instance.btitle)
        instance.bpub_date = validated_data.get('bpub_date', instance.bpub_date)
        instance.bread = validated_data.get('bread', instance.bread)
        instance.bcomment = validated_data.get('bcomment', instance.bcomment)
        instance.save()
        return instance

    两点说明:

    1) 在对序列化器进行save()保存时,可以额外传递数据,这些数据可以在create()和update()中的validated_data参数获取到

    serializer.save(owner=request.user)

    2)默认序列化器必须传递所有required的字段,否则会抛出验证异常。但是我们可以使用partial参数来允许部分字段更新

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

     2.模型类序列化器
    上边我们自定义序列化器需要将好多字段在序列化器中定义,比较复杂。模型类序列化器提供了更快捷的方式帮我们更快的提供了序列化器。

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

    基于模型类自动生成一系列字段
    基于模型类自动为Serializer生成validators,比如unique_together
    包含默认的create()和update()的实现
    1. 定义
    比如我们创建一个BookInfoSerializer

    class BookInfoSerializer(serializers.ModelSerializer):
        """图书数据序列化器"""
        class Meta:
            model = BookInfo
            fields = '__all__'
    • model 指明参照哪个模型类
    • fields 指明为模型类的哪些字段生成

    我们可以在python manage.py shell中查看自动生成的BookInfoSerializer的具体实现

    from booktest.serializers import BookInfoSerializer
    serializer = BookInfoSerializer()
    serializer
    BookInfoSerializer():
        id = IntegerField(label='ID', read_only=True)
        btitle = CharField(label='名称', max_length=20)
        bpub_date = DateField(allow_null=True, label='发布日期', required=False)
        bread = IntegerField(label='阅读量', max_value=2147483647, min_value=-2147483648, required=False)
        bcomment = IntegerField(label='评论量', max_value=2147483647, min_value=-2147483648, required=False)
        image = ImageField(allow_null=True, label='图片', max_length=100, required=False)

    2. 指定字段

    1. 使用fields来明确字段,__all__表名包含所有字段,也可以写明具体哪些字段,如

    class BookInfoSerializer(serializers.ModelSerializer):
        """图书数据序列化器"""
        class Meta:
            model = BookInfo
            fields = ('id', 'btitle', 'bpub_date')

    2.使用exclude可以明确排除掉哪些字段

    class BookInfoSerializer(serializers.ModelSerializer):
        """图书数据序列化器"""
        class Meta:
            model = BookInfo
            exclude = ('image',)

    3. 默认ModelSerializer使用主键作为关联字段,但是我们可以使用depth来简单的生成嵌套表示,depth应该是整数,表明嵌套的层级数量。如:

    class HeroInfoSerializer2(serializers.ModelSerializer):
        class Meta:
            model = HeroInfo
            fields = '__all__'
            depth = 1

    形成的序列化器如下:

    HeroInfoSerializer():
        id = IntegerField(label='ID', read_only=True)
        hname = CharField(label='名称', max_length=20)
        hgender = ChoiceField(choices=((0, 'male'), (1, 'female')), label='性别', required=False, validators=[<django.core.valators.MinValueValidator object>, <django.core.validators.MaxValueValidator object>])
        hcomment = CharField(allow_null=True, label='描述信息', max_length=200, required=False)
        hbook = NestedSerializer(read_only=True):
            id = IntegerField(label='ID', read_only=True)
            btitle = CharField(label='名称', max_length=20)
            bpub_date = DateField(allow_null=True, label='发布日期', required=False)
            bread = IntegerField(label='阅读量', max_value=2147483647, min_value=-2147483648, required=False)
            bcomment = IntegerField(label='评论量', max_value=2147483647, min_value=-2147483648, required=False)
            image = ImageField(allow_null=True, label='图片', max_length=100, required=False)

    4. 显示指明字段,如:

    class HeroInfoSerializer(serializers.ModelSerializer):
        hbook = BookInfoSerializer()
     
        class Meta:
            model = HeroInfo
            fields = ('id', 'hname', 'hgender', 'hcomment', 'hbook')

    5.指明只读字段

    可以通过read_only_fields指明只读字段,即仅用于序列化输出的字段

    class BookInfoSerializer(serializers.ModelSerializer):
        """图书数据序列化器"""
        class Meta:
            model = BookInfo
            fields = ('id', 'btitle', 'bpub_date''bread', 'bcomment')
            read_only_fields = ('id', 'bread', 'bcomment')

    3. 添加额外参数

    我们可以使用extra_kwargs参数为ModelSerializer添加或修改原有的选项参数

    class BookInfoSerializer(serializers.ModelSerializer):
        """图书数据序列化器"""
        class Meta:
            model = BookInfo
            fields = ('id', 'btitle', 'bpub_date', 'bread', 'bcomment')
            extra_kwargs = {
                'bread': {'min_value': 0, 'required': True},
                'bcomment': {'min_value': 0, 'required': True},
            }
     
    # BookInfoSerializer():
    #    id = IntegerField(label='ID', read_only=True)
    #    btitle = CharField(label='名称', max_length=20)
    #    bpub_date = DateField(allow_null=True, label='发布日期', required=False)
    #    bread = IntegerField(label='阅读量', max_value=2147483647, min_value=0, required=True)
    #    bcomment = IntegerField(label='评论量', max_value=2147483647, min_value=0, required=True)--------------------- 

    作者:qq_25068917
    来源:CSDN
    原文:https://blog.csdn.net/qq_25068917/article/details/81077145
    版权声明:本文为博主原创文章,转载请附上博文链接!

  • 相关阅读:
    Hibernate第二天:Hibernate的一级缓存、其他的API
    Hibernate第一天(Hibernate的环境搭建、Hibernate的API、Hibernate的CRUD)
    数据结构基础(一)数组,矩阵
    MyEclipseSVN插件百度云下载
    修改CentOS主机名
    Word中增加仿宋GB-2312字体
    Linux系统常用基本命令总结
    SpringBoot视频教程 百度云
    VM虚拟机中linux centOS 联网单网卡配置教程
    Java正则表达式匹配日期及基本使用
  • 原文地址:https://www.cnblogs.com/songhuasheng/p/10081136.html
Copyright © 2020-2023  润新知