drf 之 serializer序列化器

1. 简单了解

- 序列化字段处理原始值和内部数据类型之间的转换。它们还处理验证输入值,
以及从其母对象检索和设置值。

导入:from rest_framework import seriaalizers

使用:serializers.<FieldName>
有 serializers.Serializer 和 serializers.ModelSerializer,和 django 的 form 差不多


- 序列化
把Python中对象转换为json格式字符串

- 反序列化
把json格式转为为Python对象。

- 序列化两大功能:
对请求数据进行验证
对Queryset对象进行序列化

形象过程

'''
序列化器:
实质: 定义了一个叫做 xx序列化器 的 <类>,  
作用: 
	 1. 实现 <json> 和 <对象> 的相互转化
	 2. 实现对 前端数据 的校验功能 
序列化       读取(read_only)obj(数据库)   -> json -> 前端
反序列化	 前端数据校验(json)  ->  obj  -> (write_only)(数据库)
'''

2. 核心参数

- read_only
只读字段被包含在API输出中,但是在创建或更新操作期间不应该包含在输入中。在序列化器输入中不正确包含的任何“只读”字段将被忽略。
只是接受,不会提交字段 例如 时间 字段

- write_only
将其设置为 True,以确保在更新或创建实例时可用该字段,但在序列化时不会序列化此字段

required
必填字段

- validators
验证字段的存在,简短的验证,例如唯一性验证,配合 UniqueValidator 等。

- error_messages
错误信息

- label label
一个短的文本字符串,可以用作HTML表单字段或其他描述元素的字段名。可用于 drf 的文档接口中友好描述

- initial	一个值,应该用于预填充HTML表单字段的值。你可以通过一个可调用的方法,就像你可以使用任何常规的Django字段一样

	'''
	import datetime
	from rest_framework import serializers
	class ExampleSerializer(serializers.Serializer):
		day = serializers.DateField(initial=datetime.date.today)
	'''
	
- style
控制浏览器如何渲染此字段,例如 'input_type' 和 'base_template'

3. 核心字段

....

4. 序列化

serializer = SnippetSerializer(snippet)其中snippet是一个model类的实例

serializer.data调用序列化类的 data静态方法就可以得到序列化后的对应数据。
序列化类也可以传入Model类的querysets类型来序列化多组数据,只需要将其中的一个many参数改为True,即many=True,

serializer = SnippetSerializer(querysets)

serializer.data
通过以上的操作得到的数据类型为python的基本数据类型,可以通过一下方式将数据转化成json数据
from rest_framework.renderers import JSONRenderer
content = JSONRenderer().render(serializer.data)

5. 反序列化

首先我们需要将json数据转化成流,然后在转化成python基本的数据结构
import io
stream = io.BytesIO(content)通过io模块将json数据类型转化成流

data = JSONParser().parse(stream)将流转化成python基本的数据结构

serializer = SnippetSerializer(data=data)反序列化数据

serializer.is_valid()校验数据

serializer.validated_data查看校验通过的数据

serializer.save()保存数据(调用序列化类的creat方法)
ModelSerializer同Serializer很相似,只不过建立了序列化类和Model类的强联系,简化了代码,提高了代码的可重用性。

6. serializer.Serializer示例(手机号登录验证)

import re
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from django_redis import get_redis_connection



# 手机号验证
def phone_validator(value):
	if not re.match(r"^(1[3|4|5|6|7|8|9])\d{9}$", value):
		raise ValidationError('手机格式错误')
		
class MessageSerializer(serializers.Serializer):
	phone = serializers.CharField(label="手机号", validators=[phone_validator])

		
# 短信验证码验证
class LoginSerializer(serializers.Serializer):
	"""
	1. 校验手机号是否合法
	2. 校验验证码,redis
		- 无验证码
		- 有验证码,输入错误
		- 有验证码,成功
	"""
	phone = serializers.CharField(label="手机号", validators=[phone_validator])
	code = serializers.CharField(label="验证码", )
	nickName = serializers.CharField(label="用户昵称")
	avatarUrl = serializers.CharField(label="用户昵称")

	def validate_code(self, value):
		if len(value) != 4:
			raise ValidationError('验证码格式错误')
		if not value.isdecimal():
			raise ValidationError('验证码格式错误')

		phone = self.initial_data.get('phone')
		conn = get_redis_connection()
		code = conn.get(phone)
		if not code:
			raise ValidationError('验证码过期')
		if value != code.decode('utf-8'):
			raise ValidationError('验证码错误')
		return value

7. 自定义钩子函数:

# 单一校验,value为校验数据,注意一定要以 validate_<Filed name> 为名称
def validate_code(self, value):
	if len(value) != 4:
		raise ValidationError('验证码格式错误')
	if not value.isdecimal():
		raise ValidationError('验证码格式错误')

	phone = self.initial_data.get('phone')
	conn = get_redis_connection()
	code = conn.get(phone)
	if not code:
		raise ValidationError('验证码过期')
	if value != code.decode('utf-8'):
		raise ValidationError('验证码错误')
	return value

8. serializer.Serializer 嵌套使用

"""
# 需要校验的前端数据
data: {
		cover_url:that.data.imagelist[0].cos_path,
		token:{"token":that.data.userinfo.token},
		imagelist: that.data.imageInfo,
		content:that.data.content,
		location: that.data.localPath,
		topic: that.data.topicId,
	  },
# 实际数据
{
	'imagelist': [OrderedDict([('cos_path',
		'http://images-1301082770.cos.ap-beijing.myqcloud.com/publish/x8g2y1fl1579067901828.jpg'), (
		'key', 'x8g2y1fl1579067901828.jpg')]), OrderedDict([('cos_path',
		'http://images-1301082770.cos.ap-beijing.myqcloud.com/publish/gljj9lnc1579067901834.jpg'), (
		'key', 'gljj9lnc1579067901834.jpg')])],
	'token': OrderedDict([('token', '738089ad-e070-4991-bddc-bba45a262780')]),
	'cover_url': 'http://images-1301082770.cos.ap-beijing.myqcloud.com/publish/x8g2y1fl1579067901828.jpg'
	'content': '今天天气有雾霾。。。。。',
	'location': '北京市昌平区政府街19号',
	'topic': < Topic: 大雪之日 >
}
"""

# 嵌套使用,many=True,前提条件:imagelist有至少两条校验数据
class UserinfoSerializer(serializers.Serializer):
	token = serializers.CharField()


class ImageSerializer(serializers.Serializer):
	cos_path = serializers.CharField()
	key = serializers.CharField()


class ArticleDetailSerializer(serializers.ModelSerializer):
	imagelist = ImageSerializer(many=True)
	token = UserinfoSerializer()
	cover_url = serializers.CharField()

	class Meta:
		model = models.ArticleDetail
		exclude = ['article', 'mention_user', 'browse_user',
				   'browse_count', 'share_count', 'enshrine_count', 'comment_count']

9. 对所有字段进行扩展验证

我们需要在序列化器中定义一个名字为(validate())的方法,来补充对所有字段进行扩展验证的逻辑。

def validate(self, attrs):

	'''
	对多个字段进行验证
	:param attrs: 前段传递的字典数据
	:return: 验证成功:返回attrs;验证失败:返回错误信息
	'''
	# 验证逻辑:这里需要验证的是bread大于bcomment
    read = attrs.get('read')
    comment = attrs.get('comment')

    if bread < bcomment:
        # 失败
        raise serializers.ValidationError('read需要大于comment')

    # 成功
    return attrs

10. 验证后的字典数据 ---> 模型数据(添加或者修改)

这里,我们已将到了数据反序列化的最后一步操作了。

同样,我们也应该知道,将验证后的字典数据转模型数据的实质,其实就是将验证过的字典数据添加到数据库中。

那么,对于将数据保存到数据库中的操作一共就两种:一种是添加数据的操作,一种是修改数据的操作。

11. 添加数据(create)

- 我们,想要添加数据,就需要在序列化器类中定义一个create()方法。
在这里,我们需要返回添加的数据给前端,因为我们定义的API是遵守RESTful设计风格的。


- 当我们创建序列化对象时,只传递data数据,就表示我们需要向数据库里添加数据。
所以,当序列化器对象调用save()方法时,会自动调用序列化器类中的create()方法。



def create(self, validated_data):
	'''
	序列化器进行新增数据的方法
	:param validated_data: 通过验证的字典数据
	:return:根据RESTful设计方式,需要将添加的数据返回
	'''
	return ORM添加数据的语句(模型类.objects.create(**validated_data))
	
	
例如:

def create(self, validated_data):
	'''
	序列化器进行新增数据的方法
	:param validated_data: 通过验证的字典数据
	:return:根据RESTful设计方式,需要将添加的数据返回
	'''

	return BookInfo.objects.create(**validated_data)

12. 修改数据

我们,想要修改数据时,需要在序列化器类中定义一个update()方法。

def update(self, instance, validated_data):
    '''
    序列化器进行修改数据的方法
    :param instance: 需要修改的模型对象
    :param validated_data: 前端传递的字典数据
    :return: 根据RESTful设计方式,需要将修改的数据返回
    '''
    
    # 新值覆盖旧值
    instance.字段名 = validated_data.get(字段名)
    instance.save()
    return instance
	
	
例如:

def update(self, instance, validated_data):
	'''
	序列化器进行修改数据的方法
	:param instance: 需要修改的模型对象
	:param validated_data: 前端传递的字典数据
	:return: 根据RESTful设计方式,需要将修改的数据返回
	'''

	# 新值覆盖旧值
	instance.title = validated_data.get('title')
	instance.pub_date = validated_data.get('pub_date')
	instance.read = validated_data.get('read')
	instance.comment = validated_data.get('comment')
	instance.save()

	return instance
	
我们同样也需要将修改的数据返回给前端。


-注意:

'''
当我们创建序列化器对象时,传递了instance和data数据,就表示我们需要进行数据的修改操作。

所以,当我们使用序列化器对象调用save()方法时,会自动调用序列化器类中的update()方法。
'''
posted @ 2020-02-07 23:55  阿浪阿浪  阅读(760)  评论(0编辑  收藏  举报