zoukankan      html  css  js  c++  java
  • DRF中的序列化器

    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)
  • 相关阅读:
    OSG-提示“error reading file e:1.jpg file not handled”
    OSG-加载地球文件报0x00000005错误,提示error reading file simple.earth file not handled
    QT-找开工程后,最上方提示the code model could not parse an included file, which might lead to incorrect code completion and highlighting, for example.
    我的书《Unity3D动作游戏开发实战》出版了
    java中无符号类型的第三方库jOOU
    Windows批处理备份mysql数据
    使用 DevTools 时,通用Mapper经常会出现 class x.x.A cannot be cast to x.x.A
    Java版本,Java版本MongoDB驱动,驱动与MongoDB数据库,Spring之间的兼容性
    Jrebel本地激活方法
    wget下载指定网站目录下的所有内容
  • 原文地址:https://www.cnblogs.com/cainingning/p/9965689.html
Copyright © 2011-2022 走看看