序列化与反序列化介绍

在日常开发中,会从别的API获取数据或者自己写API提供数据,数据格式一般都是采用 JSON格式。这期间就会涉及两个专业术语:

  • 序列化:将python对象转json

  • 反序列化:将json转为python对象

之前常用三种序列化方式

之前经常用json模式完成序列化与反序列化操作:

  • 序列化应用场景示例:用ORM查询数据,采用JSON格式API返回数据。

  • 反序列化应用场景示例:从别的API获取数据,在Python里处理。

import json 
# 序列化
computer = {"主机":5000,"显示器":1000,"鼠标":60,"键盘":150}
json.dumps(computer)
# 反序列化
json.loads(json_obj)

Serializers是Django内置的一个序列化器,可直接将Python对象转为JSON格式,但不支 持反序列化。

from django.core import serializers 
obj = User.objects.all()
data = serializers.serialize('json', obj)

JsonResponse模块自动将Python对象转为JSON对象并响应。

DRF序列化器三种类型

DRF中有一个serializers模块专门负责数据序列化,DRF提供的方案更先进、更高级别的序列化方案。

序列化器支持三种类型:

  • Serializer:对Model(数据模型)进行序列化,需自定义字段映射。

  • ModelSerializer:对Model进行序列化,会自动生成字段和验证规则,默认还包含简单的create()和update()方法。

  • HyperlinkedModelSerializer:与ModelSerializer类似,只不过使用超链接来表示关系而不是主键ID。

DRF序列化器:Serializer

DRF序列化器:小结

序列化器工作流程:

序列化(读数据):视图里通过ORM从数据库获取数据查询集对象-> 数据传入序列化器-> 序列化器将数据进行序列化-> 调用序列化器的.data获取数据-> 响应返回前端

反序列化(写数据):视图获取前端提交的数据-> 数据传入序列化器-> 调用序列化器 的.is_valid方法进行效验-> 调用序列化器的.save()方法保存数据

序列化器常用方法与属性:

  • serializer.is_valid():调用序列化器验证是否通过,传入raise_exception=True可以在 验证失败时由DRF响应400异常。

  • serializer.errors:获取反序列化器验证的错误信息

  • serializer.data:获取序列化器返回的数据

  • serializer.save():将验证通过的数据保存到数据库(ORM操作)

DRF序列化器:序列化器参数

DRF序列化器:扩展验证规则

如果常用参数无法满足验证要求时,可通过钩子方法扩展验证规则。

局部钩子:validate_字段名(self, 字段值)

全局钩子:validate(self, 所有校验的数据字典)

如果钩子无法满足需要,可以自定义验证器,更灵活。

在序列化类外面定义验证器,使用validators参数指定验证器。

DRF序列化器:ModelSerializer

ModelSerializer 类型不需要自定义字段映射和定义create、update方法,使用起来方便很多!

Meta类常用属性:

  • fields:显示所有或指定字段

  • exclude:排除某个字段,元组格式,不能与fields同时用

  • read_only_fields:只读字段,即只用于序列化,不支持修改

  • extra_kwargs:添加或修改原有的字段参数,字典格式

  • depth:根据关联的数据递归显示,一般是多表

DRF序列化器:HyperModelSerializer

与MedelSerializer使用方法一样。只不过它使用超链接来表示关系而不是主键ID。

示例:基于前面用户管理修改

# 更改序列化器
class UserSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = User
fields = "__all__"

# 更改视图
user_ser = UserSerializer(queryset, many=True, context={'request': request})

# 更改路由
re_path('^api/user/$', views.UserView.as_view(), name="user-detail"),
re_path('^api/user/(?P<pk>\d+)/$', views.UserView.as_view(), name="user-detail"),

DRF序列化器关联表显示

例如:应用发布系统项目涉及表

一对多:一个项目有多个应用,一个应用只能属于一个项目

多对多:一个应用部署到多台服务器,一个服务器部署多个应用

1、定义数据模型

class Project(models.Model):
name = models.CharField(max_length=30)
class App(models.Model):
name = models.CharField(max_length=30)
project = models.ForeignKey(Project, on_delete=models.CASCADE) # 一对多
class Server(models.Model):
hostname = models.CharField(max_length=30)
ip = models.GenericIPAddressField()
app = models.ManyToManyField(App) # 多对多

2、定义序列化器

class ProjectSerializer(serializers.ModelSerializer):
class Meta:
model = Project
fields = "__all__"
class AppSerializer(serializers.ModelSerializer):
class Meta:
model = App
fields = "__all__"
class ServerSerializer(serializers.ModelSerializer):
class Meta:
model = Server
fields = "__all__"

3、定义视图

4、定义路由

re_path('^api/project/$', views.ProjectView.as_view()), 
re_path('^api/app/$', views.AppView.as_view()),
re_path('^api/server/$', views.ServerView.as_view()),

5、添加测试数据

创建项目:
from myapp_api.models import Project, App, Server
Project.objects.create(name="电商")
Project.objects.create(name="教育")
创建应用并指定项目:
project_obj = Project.objects.get(name="电商")
App.objects.create(name="portal", project=project_obj)
App.objects.create(name="gateway", project=project_obj)
创建服务器:
Server.objects.create(hostname="test1", ip="192.168.31.10")
Server.objects.create(hostname="test2", ip="192.168.31.11")
将应用部署到服务器:
app_obj = App.objects.get(name="portal")
server_obj = Server.objects.get(hostname="test1")
server_obj.app.add(app_obj)

序列化器返回是当前模型中的字段,如果字段是外键时,返回的是外键对应id,如图所 示,如果想要显示外键对应的详细信息如何做呢?

有两种方法:

  • 定义字段为外键对应序列化类:例如project=ProjectSerializer(read_only=True), 这种适合针对某个外键字段。

  • 序列化类中Meta类启用depth:深度获取关联表数据,这种所有外键都会显示出来。

一对多

# 方法1
class AppSerializer(serializers.ModelSerializer):
project = ProjectSerializer(read_only=True) # 一对多,返回关联的项目详情
class Meta:
model = App
fields = "__all__"
# 方法2
class AppSerializer(serializers.ModelSerializer):
class Meta:
model = App
fields = "__all__"
depth = 1

多对多

# 方法1
class ServerSerializer(serializers.ModelSerializer):
app = AppSerializer(many=True) # 多对多,返回关联的应用详情
class Meta:
model = Server
fields = "__all__"
# 方法2
class ServerSerializer(serializers.ModelSerializer):
class Meta:
model = Server
fields = "__all__"
depth = 1

DRF序列化器:SerializerMethodField

DRF序列化器默认仅返回数据模型中已存在资源,如果想新增返回字段或者二次处理,该 如何操作呢?用SerializerMethodFiled

示例:给项目API增加一个字段,这个字段数据可从别的表中获取

改变序列化和反序列化行为

可以通过重写下面两个方法改变序列化和反序列化的行为:

  • to_internal_value():处理反序列化的输入数据,自动转换Python对象,方便处理。

  • to_representation():处理序列化数据的输出。

示例:如果提交API的数据与序列化器要求的格式不符合,序列化器就会出现错误。 这时就可以重写to_internal_value()方法只提取出我们需要的数据。

示例:希望给返回的数据添加一个统计应用数量的字段