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_namedef __str__(self):return self.hname
根据模型类定义出相对应的序列化器类:需要导包:
**from rest_framework import serializers**from rest_framework import serializersclass 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()方法```pythonclass 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**`标志传递给嵌套序列化程序。```pythonclass 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()# Falseserializer.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 = Userfields = ['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 userdef 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.profileinstance.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 = Userfields = ['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
