1. 序列化和反序列化的由来:
- 前后端分离的核心思想之一,就是两端交互不通过模板语言,只传输需要的数据。因此问题就来了。
- 在 Django 程序的运行过程中,变量都是存储在服务器的内存中;更要命的是,后端 Django 程序中存储的是 Python 变量,而前端的浏览器中是 Javascript 变量,这两者是无法直接通过你家的网线进行传递和交流的。因此需要规定一个“标准格式”,前后端都根据这个标准格式,对资源进行保存、读取、传输等操作。
- JSON 就是这种标准格式之一。它很轻量,表示出来就是个字符串,可以直接被几乎所有的语言读取,非常方便。
把变量从内存中变成可存储或传输的过程称之为序列化,反过来把变量内容从序列化的对象重新读到内存里称之为反序列化
2. 创建序列化器
- 在子应用中,根据
**models.py**
中的模型类新建**Seriaizer.py**
文件,创建了序列化器文件后不能删除**models.py**
所有的序列化器都是
**serializers.Serializer**
的子类,但是定义字段时都是调用**serializers包**
下的字段类。2.1 根据数据库模型类定义出相应的序列化器
已知模型类
**BookInfo,HeroInfo**
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, 'male'),
(1, 'female')
)
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
根据模型类定义出相对应的序列化器类:需要导包:
**from rest_framework import serializers**
from rest_framework import serializers
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)
# 1V多关联对象字段定义
heroinfo_set = serializers.PrimarKeyRelatedField(label='英雄', read_only=true,many=True)
class HeroInfoSerializers(serializers.Serializer):
GENDER_CHOICES = (
(0, 'male'),
(1, 'female')
)
id = serializers.IntegerField(label="ID", read_only=True)
hname = serializers.CharField(label='名字', max_length=20)
hgender = serializers.ChoiceField(
label='性别', choices=GENDER_CHOICES, required=False)
hcomment = serializers.CharField(
label='描述信息', max_length=200, required=False, allow_null=True)
# 多V1关联对象字段定义
# hbook = serializers.PrimaryKeyRelatedField(label='图书',read_only=True)
# hbook = serializers.HyperlinkedRelatedField(label='图书', read_only=True, view_name='books-detail')
hbook = BookRelateField(read_only=True)
2.1.1 字段类型
2.1.2 字段通用选项
序列化器类中的字段都含有以下选项:
字段选项 | 字段选项说明 |
---|---|
read_only | 表明该字段仅用于序列化输出,默认False |
write_only | 表明该字段仅用于反序列化输入,默认False |
required | 表明该字段在反序列化时必须输入,默认True |
default | 反序列化时使用的默认值 |
allow_null | 表明该字段是否允许传入None,默认False |
source | |
validators | 该字段使用的验证器 |
error_messages | 包含错误编号与错误信息的字典 |
label | 用于HTML展示API页面时,显示的字段名称 |
help_text | 用于HTML展示API页面时,显示的字段帮助提示信息 |
initial | |
style |
2.1.3 元类Meta
3. 使用序列化器
3.1 实例化Serializer类
Serializer的构造方法为:
Serializer(instance=None, data=empty, **kwarg)
说明:
- 用于序列化时,将模型类对象赋值给
instance
参数,无需给data
参数赋值。假如instance的值为多个数据对象的字典,则添加
many=True
。
- 用于反序列化时,将要被反序列化的数据传入
data
参数,不传入instance
。 - 除了
**instance**
和**data**
参数外,在构造**Serializer**
对象时,还可通过**context**
参数额外添加数据。serializer = AccountSerializer(account, context={'request': request})
# 通过context参数附加的数据,可以通过Serializer对象的context属性获取。
3.2 序列化
3.2.1 序列化单个数据对象
def get(self,request):
#获取单个数据对象
book = BookInfo.objects.get(id=2)
serializer = BookInfoSerializer(book) #序列化器初始化
# return JsonResponse(ser.data) # 当ser.data是单个数据时,safe可以用默认值
return Response(ser.data)
2.3.2 序列化多个数据对象
def get(self, request):
# 1、查询所有图书对象
books = BookInfo.objects.all()
# 对序列化器实例化,其中many=True 声明books对象包含多个数据,若返回一个对象,则不需many参数
ser = BookSerialzier(books, many=True) # 也可写为 ser = BookSerialzier(instance=books,many=True)
# ser.data :通过data属性获取序列化后的数据,safe=False:声明data可以为任何被转换为Json格式的对象,默认为True时data类型为字典类型
# return JsonResponse(ser.data, safe=False)——这种是django自带的响应
return Response(ser.data) # 使用DRF的响应对象,内部已经实现了反序列化功能
3.3 反序列化
- 使用序列化器进行反序列化时,需要对数据进行验证后,才能获取验证成功的数据或保存成模型类对象。分为两步:
- 数据校验
- 数据保存
版本1:能完成更新数据的类视图函数(views.py) :
def put(self,request,pk): # 函数逻辑:获取单一数据-->验证数据-->更新数据 ,能实现更新数据的功能
# 1、获取前端数据
# 没有使用序列化器时的逻辑
# data = request.body.decode()
# data_dict = json.loads(data)
# 使用了DRF框架
data_dict = request.data
# 2、验证数据
try:
book = BookInfo.objects.get(id=pk) # pk通过路由信息来获取的
except:
return Response({'error': '错误信息'}, status=400)
ser=BookSerialzier(book,data=data_dict)
if ser.is_valid():
# 3、更新数据
ser.save()
# 4、返回结果
return JsonResponse(ser.data)
else:
return Responce(ser.errors,status=status.HTTP_400_BAD_REQUEST)
版本2:只能完成保存功能的类视图函数:
def post(self, request):
1、获取前端数据
# data = request.body.decode()
# data_dict = json.loads(data)
data_dict = request.data
# 2、验证数据
ser= BookSerialzier(data=data_dict)
ser.is_valid() # 验证方法
# 3、保存数据
ser.save()
# 4、返回结果
return JsonResponse(ser.data)
两个版本的区别:
实例化序列化器过程:只需要保存数据则用: **ser=BookSerialzier(data=data_dict)**
;需要更新的话,需要传入模型类对象: **ser=BookSerialzier(book,data=data_dict)**
3.3.1 数据校验
def post(self, request):
# 1、获取前端json表单数据
data = request.body.decode()
data_dict = json.loads(data) #将json 对象数据转换成python字典数据
# 2、验证数据
ser= BookSerialzier(data=data_dict) #通过构造序列化器对象,并将要反序列化的数据传递给data构造参数,进行验证
ser.is_valid() # 调用验证方法,成功则返回True,否则返回False。
ser.errors # 获取错误信息,返回字典,包含了字段和字段的错误。验证成功则返回空字典
ser.validated_data #获取数据,此时的数据还只是python字典
3.3.2 验证方法
在定义序列化器时自定义验证方法(Serializer.py)
- 在定义序列化器时,指明每个字段的序列化类型和选项参数,本身就是一种验证行为。
单一字段验证。语法格式:在序列化器中实现方法:
**def validate_字段名(self,value)**
# 单一字段验证
def validate_btitle(self, value):
if value == 'python':
raise serializers.ValidationError('书名不能是python')
return value
多个字段验证:
**def validate(self,attrs) # attrs是字典**
# 多个字段验证
def validate(self, attrs):
if attrs['bread'] < attrs['bcomment']:
raise serializers.ValidationError('阅读量大于评论量')
return attrs
在字段中添加validators选项参数,也可以补充验证行为。Serializers.CharField(…,validators=[ about_django]) ```python 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) 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)
<a name="IZ2we"></a>
### 3.3.3 数据保存
如果在验证成功后,想要基于`**validate_date**`完成数据对象的创建,可以通过实现`**Serializer.py**`文件中的`**create(),update()**`两个方法实现
- create()方法
```python
class BookInfoSerializer(serializers.Serializer):
"""图书数据序列化器"""
...
def create(self, validated_data): # validated_data是python字典类型,create()方法将字典数据转换为BookInfo对象类型数据
"""新建"""
return BookInfo.objects.create(**validated_data) #实现数据对象的创建
update()方法
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() #将instance对象保存到数据库当中,如果不保存到数据库当中,则无需此操作
return instance
当执行到ser.save()时,系统会判别调用create()方法还是update()方法,源代码是:
def save(self, **kwargs):
...
if self.instance is not None:
self.instance = self.update(self.instance, validated_data)
assert self.instance is not None, (
'`update()` did not return an object instance.'
)
else:
self.instance = self.create(validated_data)
assert self.instance is not None, (
'`create()` did not return an object instance.'
)
4. 序列化器嵌套
序列化器嵌套:定义一个序列化器时,属性为另一个序列化器实例。 ```python class UserSerializer(serializers.Serializer): email = serializers.EmailField() username = serializers.CharField(max_length=100)
class CommentSerializer(serializers.Serializer): user = UserSerializer() content = serializers.CharField(max_length=200) created = serializers.DateTimeField()
如果`**UserSerializer**`可以选择接受None值,则应将`**required=False**`标志传递给嵌套序列化程序。
```python
class CommentSerializer(serializers.Serializer):
user = UserSerializer(required=False) # May be an anonymous user.
content = serializers.CharField(max_length=200)
created = serializers.DateTimeField()
如果嵌套**EditItemSerializer**
表示的是项目列表,则应将many=True标志传递给嵌套序列化程序。
class CommentSerializer(serializers.Serializer):
user = UserSerializer(required=False)
edits = EditItemSerializer(many=True) # A nested list of 'edit' items.
content = serializers.CharField(max_length=200)
created = serializers.DateTimeField()
4.1 数据校验
在处理支持反序列化数据的嵌套表示时,嵌套对象的任何错误都将嵌套在嵌套对象的字段名称下。
serializer = CommentSerializer(data={'user': {'email': 'foobar', 'username': 'doe'}, 'content': 'baz'})
serializer.is_valid()
# False
serializer.errors
# {'user': {'email': ['Enter a valid e-mail address.']}, 'created': ['This field is required.']}
同样,序列化器实例对象的validated_data属性也将包括嵌套数据结构
4.2 数据保存与更新
在嵌套的序列化器的create()方法与未含有嵌套的序列化器的create()方法不同
class UserSerializer(serializers.ModelSerializer):
profile = ProfileSerializer()
class Meta:
model = User
fields = ['username', 'email', 'profile']
def create(self, validated_data):
profile_data = validated_data.pop('profile')
user = User.objects.create(**validated_data)
Profile.objects.create(user=user, **profile_data)
return user
def update(self, instance, validated_data):
profile_data = validated_data.pop('profile')
# Unless the application properly enforces that this field is
# always set, the following could raise a `DoesNotExist`, which
# would need to be handled.
profile = instance.profile
instance.username = validated_data.get('username', instance.username)
instance.email = validated_data.get('email', instance.email)
instance.save()
profile.is_premium_member = profile_data.get(
'is_premium_member',
profile.is_premium_member
)
profile.has_support_contract = profile_data.get(
'has_support_contract',
profile.has_support_contract
)
profile.save()
return instance
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['username', 'email']
def create(self, validated_data):
return User.objects.create(**validated_data)
def update(self, instance, validated_data):
instance.username = validated_data.get('username', instance.username)
instance.email = validated_data.get('email', instance.email)
instance.save()
return instance