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方法还是需要我们自己定义的。

完。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342

推荐阅读更多精彩内容

  • Serializers 序列化器允许将诸如查询集和模型实例之类的复杂数据转换为原生 Python 数据类型,然后可...
    lkning阅读 1,012评论 0 1
  • 介绍 本教程将涵盖一个简单的PasteBin1代码高亮的Web API。整个过程,将逐一介绍REST framew...
    盛夏_264f阅读 516评论 0 0
  • 定义Serializer class BookInfoSerializer(serializers.Seriali...
    LiWei_9e4b阅读 1,039评论 0 0
  • 一个人的日子 是如此的孤寂 自从离开你 就像没有春天的森林 那一片片的落叶 像是在提醒 没有任何意义 也没有了期许...
    Eva小姐姐阅读 104评论 0 2
  • 思维迟钝、唯唯诺诺、沟通能力差、人际关系差,上不了台面、撑不了场子,这可能是我们很多人给“内向”贴的标签,在职场中...
    四方fancy阅读 212评论 0 2