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)

 

 

posted on 2019-01-26 10:45  cai128118*  阅读(233)  评论(0编辑  收藏  举报

导航