DjangoRest framework-简化API开发:Serializer序列化器的使用

前言

使用Serializer序列化器之前,首先应理解并遵守RESTful的API设计风格。
推荐一篇能比较好了解RESTful的文章:RESTful API 最佳实践

REST API接口核心任务

开发REST API接口时,常做的是三件事:

  1. 将请求的数据(如JSON格式)转换为模型类对象
  2. 操作数据库
  3. 将模型类对象转换为响应的数据(如JSON格式)

而数据格式化转换的操作往往是繁琐且重复的。

序列化Serialization

为了简化这些操作,引入新的功能:序列化反序列化

1. 序列化

可将序列化的含义简单理解为:

将程序中的一个数据结构类型转换为其他格式(字典、JSON、XML等),例如将Django中的模型类对象装换为JSON字符串,这个转换过程我们称为序列化。

比如在一个查询数据的API中,我们查询数据库数据得到模型类对象,将其按照格式重组并转换为json的过程就是序列化:

from . models import BookInfo

def get(request):
    # 数据库中查询获取模型类对象
    queryset = BookInfo.objects.all()
    book_list = []
    # 序列化
    for book in queryset:
        book_list.append({
            'id': book.id,
            'btitle': book.btitle,
            'bpub_date': book.bpub_date,
            'bread': book.bread,
            'bcomment': book.bcomment,
       })
  return JsonResponse(book_list, safe=False)

2. 反序列化

相反的,可将反序列化的含义简单理解为:

将其他格式(字典、JSON、XML等)转换为程序中的数据,例如将JSON字符串转换为Django中的模型类对象,这个过程我们称为反序列化。

比如在一个新增数据的API中,我们接收前端传递来的json数据,将其转换为模型类对象的过程就是反序列化:

from . models import BookInfo

def post(self, request):
    data = request.body.decode()
    # 反序列化
    data_dict = json.loads(data)
    title = data_dict.get("btitle")
    pub_date = data_dict.get("bpub_date")
    book = BookInfo.objects.create(btitle=title, bpub_date=pub_date)
  )
  # 又再次序列化返回
    return http.JsonResponse({
            'id': book.id,
            'btitle': book.btitle,
            'bpub_date': book.bpub_date
     })

可以看出,在开发REST API接口时,我们在视图中需要做的最核心的事是:

  • 将数据库数据序列化为前端所需要的格式,并返回;

  • 将前端发送的数据反序列化为模型类对象,并保存到数据库中。

Django REST framework

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

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

Django REST framework(DRF) 提供了Serializer可以快速根据 Django ORM 或者其它库自动序列化/反序列化可以帮助我们简化上述部分代码编写,大大提高开发速度。

环境安装和配置

  1. 安装DRF
    pip install djangorestframework
  2. 添加应用
    rest_framework添加到配置文件INSTALLED_APPS中:
INSTALLED_APPS = [
    'rest_framework',
]

配置完成。

Serializer序列化器的使用

接下来的Serialize使用示例基于以下书籍数据模型类、与其关联的英雄数据模型类:

from django.db import models

class BookInfo(models.Model):
    btitle = models.CharField(max_length=20, verbose_name='名称')
    bpub_date = models.DateField(verbose_name='发布日期')
    bread = models.IntegerField(default=0, verbose_name='阅读量')
    bcomment = models.IntegerField(default=0, verbose_name='评论量')
    is_delete = models.BooleanField(default=False, verbose_name='逻辑删除')

 class Meta:
        db_table = 'tb_books'  # 指明数据库表名
        verbose_name = '图书'  # 在admin站点中显示的名称
        verbose_name_plural = verbose_name  # 显示的复数名称

    def __str__(self):
        """定义每个数据对象的显示信息"""
        return self.btitle


class HeroInfo(models.Model):
    GENDER_CHOICES = (
        (0, 'female'),
        (1, 'male')
    )
    hname = models.CharField(max_length=20, verbose_name='名称')
    hgender = models.SmallIntegerField(choices=GENDER_CHOICES, default=0, verbose_name='性别')
    hcomment = models.CharField(max_length=200, null=True, verbose_name='描述信息')
    hbook = models.ForeignKey(BookInfo, on_delete=models.CASCADE, verbose_name='图书')  # 外键
    is_delete = models.BooleanField(default=False, verbose_name='逻辑删除')

    class Meta:
        db_table = 'tb_heros'
        verbose_name = '英雄'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.hname

Serializer类有两种,一种是基于Serializer类的基本序列化器,另一种是封装度更高,使用更简单的模型类ModelSerializer序列化器,其实为了效率,我们更多使用的是ModelSerializer类。

1. 基于Serializer类的序列化器

在app中创建新文件serializers,在其内定义序列化器,继承于Serializer类。

(1)定义Serializer序列化器

为了简便,假设我们需要的字段只包括标题和发布时间,定义如下:

from rest_framework import serializers

class BookSerializes(serializers.Serializer):
    btitle = serializers.CharField(min_length=0, max_length=20)
    bpub_date = serializers.DateField()

    # 使用外键连接时的字段
    # heroinfo_set = serializers.StringRelatedField(read_only=True,many=True)   #关联__str__方法指定字段
    # heroinfo_set = serializers.PrimaryKeyRelatedField(read_only=True, many=True)   #关联主键对象

(2)字段与参数

  • 字段命名需要与模型类中的字段名一致;
  • 对应字段类型需要与模型类中属于同一类;
  • 选项参数:
    用于后面将介绍的反序列化is_valid()数据验证
选择项名 作用
max_length 最大长度
min_lenght 最小长度
allow_blank 是否允许为空
min_value 最小值
max_value 最大值
  • 核心参数:
参数名 说明
read_only 表明该字段仅用于序列化输出,默认False
write_only 表明该字段仅用于反序列化输入,默认False
required 表明该字段在反序列化时必须输入,默认True
default 反序列化时使用的默认值
allow_null 表明该字段是否允许传入None,默认False

这里的字段名必须与模型类的字段名相同,数据类型也需与模型类属同一类模型。关于完整字段类型及参数选项的说明可参考官方文档:https://www.django-rest-framework.org/api-guide/fields/

(3)序列化的使用
使用serializer对象直接构建序列化数据

from .serializers import BookSerializes
from . models import BookInfo

def get(self,request):
    books = BookInfo.objects.all()
    # 构建序列化对象。(非单个数据时,需要many=True参数)
    serializer = BookSerializes(books, many=True)
    # data方法可直接返回序列化的json数据
    data = serializer.data
    return http.JsonResponse(data, safe=False)

返回的序列化data数据结构如下:

[OrderedDict([('btitle', '射雕英雄传'), ('bpub_date', '1980-05-01')]), OrderedDict([('btitle', '天龙八部'), ('bpub_date', '1986-07-24')]), OrderedDict([('btitle', '笑傲江湖'), ('bpub_date', '1995-12-24')]), OrderedDict([('btitle', '雪山飞狐'), ('bpub_date', '1987-11-11')]]

对比最初的自行写代码序列化数据,简洁了很多。
需要注意的是:

  • many参数:如果关联的对象数据不是只有一个,而是包含多个数据,需要使用many=True指定。

(4)反序列化的使用

反序列化相对序列化复杂一些,因为接收前端数据后,需要先对数据进行验证后,才能获取验证成功的数据或保存成模型类对象。

在视图函数中调用验证方法is_valid()save()法会自动调用
定义的序列化器中的validate验证和createsave反序列化方法。

  • 在获取反序列化的数据前,必须调用is_valid()方法进行验证,验证成功返回True,否则返回False。
  • is_valid()方法校验的条件有两个。
    1. 定义BookSerializes序列化器类的时传入的选项参数,如max_length=20限定字段最大长度为20.
    2. 自定义validate()方法,更加灵活可配置,is_valid()在对比字段选项参数后,将自动调用validate()的过滤方法,示例如下:
class BookSerializes(serializers.Serializer):
    ...

    def validate(self, attrs):
        if attrs["btitle"] == "python":
            raise serializers.ValidationError("书名不允许")
        if len(attrs["btitle"]) < 2:
            raise serializers.ValidationError("书名长度太小")
        return attrs

attrs参数为字典类型,自动接收所有我们定义的序列化字段数据。

  • 封装create()update()方法
class BookSerializes(serializers.Serializer):
    ...
    def validate(self, attrs):
        if attrs["btitle"] == "python":
            raise serializers.ValidationError("书名不允许")
        if len(attrs["btitle"]) < 2:
            raise serializers.ValidationError("书名长度太小")
        return attrs
    # validated_data为完成过滤后的数据
    def create(self, validated_data):
        return BookInfo.objects.create(btitle=validated_data["btitle"],bpub_date=validated_data["bpub_date"])

    # instance参数为传递的需要修改的模型类对象
    def update(self, instance, validated_data):
        
        instance.btitle = validated_data["btitle"]
        instance.bpub_date = validated_data["bpub_date"]
        instance.save()
  • 视图函数中反序列化代码示例:

def post(self, request):
    data = request.body.decode()
    data_dict = json.loads(data)
    # 构建序列化对象,将接收到的json格式数据传给data参数
    serializer = BookSerializes(data=data_dict)
    # 验证数据是否符合要求,raise_exception=True指定不符合条件时自动抛出异常
    serializer.is_valid(raise_exception=True)
    # 根据serializer 中是否传递对象参数,自动判断调用调用序列化器中定义的create和update方法
    serializer.save()

    return http.JsonResponse(serializer.data)

2. 基于模型类ModelSerializer序列化器

相比于1中的Serializer类序列化器,DRF还提供了一个更加深度封装的ModelSerializer模型类序列化器,可以帮助我们更快的、傻瓜式的创建一个Serializer类。

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

  • 基于模型类自动生成一系列字段(可以不再自己定义字段)
  • 基于模型类自动为Serializer生成validators,比如unique_together()
  • 包含默认的create()和update()的实现(可以不再自己定义update和create方法)

(1)使用ModelSerializer定义Serializer序列化器

不再继承于serializers.Serializer类,而是继承模型序列化器类serializers.ModelSerializer,无需再自定义字段。

class BookModelSerializer(serializers.ModelSerializer):
    # 这里不用再自定义字段
    class Meta:
        model = BookInfo
        fields = '__all__'
        read_only_fields = ('id', 'bread', 'bcomment')

    def validate(self, attrs):
        if attrs['btitle'] == 'python':
            raise serializers.ValidationError('书名python错误')

        return attrs

在class Meta下可自定义类功能。

指定参照的模型类:
1)model = BookInfo

指定生成哪些字段:
1)fields = "__all__" :对应生成数据库模型类的所有字段
2)fields = ('btitle',) :只生成btitle字段,此参数类型为元祖
3)exclude = ('id' ,'btitle') : 反选,生成除id及btitle外的所有字段

指定只读字段,不参与反序列化:
1)read_only_fields = ('id', 'bread', 'bcomment')

定制化添加额外参数:
1)extra_kwargs = {字段:{参数选项:参数}}

  # 定义额外参数
  extra_kwargs = {
         'bread': {'min_value': 0, 'required': True},
         'bcomment': {'min_value': 0, 'required': True},
     }

(2)序列化的使用

序列化器定义完成后,视图函数中的使用方法完全不变。
其内部也同时封装好了之前需要我们自定义的create方法和update方法,只需在视图中使用save()方法调用即可。

如果需要使用过滤,那validate方法还是需要我们自己定义的。

完。

你可能感兴趣的:(DjangoRest framework-简化API开发:Serializer序列化器的使用)