drf序列响应速度慢(-反序列化器使用)(1)

将字典转化为对象,就是反序列化。

在开发REST API的视图中,在增、改操作中会用到反序列化操作。

增:校验请求数据 -> 执行反序列化过程 -> 保存数据库 -> 将保存的对象序列化并返回。

改:判断要修改的数据是否存在 -> 校验请求的数据 -> 执行反序列化过程 -> 保存数据库 -> 将保存的对象序列化并返回。

反序列化使用

1)定义模型,序列化器

# models.py class Column(models.Model): '''栏目''' name = models.CharField(max_length=20, unique=True, verbose_name='栏目') link_url = models.URLField(verbose_name= '链接') index = models.IntegerField(verbose_name='位置') class Meta: # 模型元选项 db_table = 'tb_column' # 在数据库中的表名,否则Django自动生成为app名字_类名 ordering = ['index'] verbose_name = '栏目' verbose_name_plural = verbose_name def __str__(self): return self.name # serializers.py class ColumnSerializer(serializers.Serializer): id = serializers.IntegerField() name = serializers.CharField(max_length=20, label='栏目') link_url = serializers.CharField(label= '链接') index = serializers.IntegerField(label='位置')

2)反序列化:

from article.serializers import ColumnSerializer # 1.模拟字典数据,真实情况是前端请求携带的参数 data = { 'id': 8, 'name': '项目案例', 'link_url': '/columns/8/', 'index': 8, 'test': 'test1', } # 传入的参数如果比序列化器的少,则校验不通过,如果传的多于序列化器,则校验通过。 # 2.创建序列化器,将字典数据给序列化器 # instance用于序列化(对象转换为字典) # data 用于反序列化(字典转换为对象) serializer = ColumnSerializer(data=data) # 3. 验证数据 serializer.is_valid(raise_exception=True) # 4. 保存 serializer.save() # serializer.data

数据验证

使用序列化器进行反序列化时,需要对数据进行验证后,才能获取验证成功的数据或保存成模型类对象,在获取反序列化的数据前,必须显式调用is_valid()方法进行验证,验证成功返回True,否则返回False。

验证失败,可以通过序列化器对象的errors属性获取错误信息,返回包含了字段和字段的错误的字典,如:

from article.serializers import ColumnSerializer # 1.模拟字典数据,真实情况是前端请求携带的参数 data = { 'name': '项目案例', 'link_url': '/columns/8/', 'index': 8, 'test': 'test1', } serializer = ColumnSerializer(data=data) serializer.is_valid() # False serializer.errors # {'id': [ErrorDetail(string='This field is required.', code='required')]}

.is_valid() 方法使用可选的 raise_exception 标志,如果存在验证错误,将会抛出 serializers.ValidationError 异常。

这些异常由rest framework提供的默认异常处理程序自动处理,默认情况下将返回 HTTP 400 Bad Request 响应。

serializer.is_valid(raise_exception=True) ''' Traceback (most recent call last): File "<console>", line 1, in <module> File "/root/.virtualenvs/qmblog_env/lib/python3.7/site-packages/rest_framework/serializers.py", line 228, in is_valid raise ValidationError(self.errors) rest_framework.exceptions.ValidationError: {'id': [ErrorDetail(string='This field is required.', code='required')]} '''

验证成功,可以通过序列化器对象的validated_data属性获取数据。

在定义序列化器时,指明每个字段的序列化类型和选项参数,本身就是一种验证行为。

字段选项验证

序列化器验证数据的第一种形式:

1)序列化器定义的数据类型,可以帮助我们在反序列化(字典转模型)时,验证传入。例如:DateField需要满足YYYY-MM-DD

IntegerField满足整形类型。

2)通过字段的选项来验证数据。例如:CharField(max_length=10, min_length=5)

IntegerField(max_value=10, min_value=1)

required=True默认是True,这也就是为什么字典中键值对比序列化器中的少,则校验不通过,改成False则会校验通过。建议显式写出来。

read_only:只用于序列化使用,反序列化时,忽略该字段,也就是不校验,也不输出。

write_only:只是用于反序列化使用,序列化的时候,忽略该字段。

自定义方法验证

1)如果我们的数据,满足类型要求,又满足选项要求,我们如果需要对数据进行进一步验证的时候,可以通过向序列化器中添加.validate_<field_name>方法来指定自定义字段级的验证,这与Django中表单字段.clean_<field_name>方法。这些方法采用单个参数,即需要验证的字段值。

例如,我们要验证栏目名称不能包含MD:

class ColumnSerializer(serializers.Serializer): id = serializers.IntegerField() name = serializers.CharField(max_length=20, label='栏目') link_url = serializers.CharField(label= '链接') index = serializers.IntegerField(label='位置') def validate_name(self, value): # value就是字段对应的值 # 名称包含MD报错 if 'MD' in value: raise serializers.ValidationError('带有敏感词汇') return value from article.serializers import ColumnSerializer # 1.模拟字典数据,真实情况是前端请求携带的参数 data = { 'id': 8, 'name': '测试MD', 'link_url': '/columns/8/', 'index': 8, } serializer = ColumnSerializer(data=data) serializer.is_valid() # False serializer.errors # {'name': [ErrorDetail(string='带有敏感词汇', code='invalid')]}

2)如果需要对多个字段进行验证,我们可以实现validate方法。

from article.serializers import ColumnSerializer # 1.模拟字典数据,真实情况是前端请求携带的参数 data = { 'id': 10, 'name': '测试', 'link_url': '/columns/8/', 'index': 8, } serializer = ColumnSerializer(data=data) serializer.is_valid() # False serializer.errors # {'non_field_errors': [ErrorDetail(string='id不能大于索引位置', code='invalid')]}

经过验证之后,我们才可以保存数据,进行入库操作。

from article.serializers import ColumnSerializer # 1.模拟字典数据,真实情况是前端请求携带的参数 data = { 'id': 8, 'name': '项目案例', 'link_url': '/columns/8/', 'index': 8, } # 传入的参数如果比序列化器的少,则校验不通过,如果传的多于序列化器,则校验通过。 # 2.创建序列化器,将字典数据给序列化器 # instance用于序列化(对象转换为字典) # data 用于反序列化(字典转换为对象) serializer = ColumnSerializer(data=data) # 3. 验证数据 serializer.is_valid(raise_exception=True) # 4. 保存 serializer.save() ''' 报错: Traceback (most recent call last): File "<console>", line 1, in <module> File "/root/.virtualenvs/qmblog_env/lib/python3.7/site-packages/rest_framework/serializers.py", line 205, in save self.instance = self.create(validated_data) File "/root/.virtualenvs/qmblog_env/lib/python3.7/site-packages/rest_framework/serializers.py", line 170, in create raise NotImplementedError('`create()` must be implemented.') NotImplementedError: `create()` must be implemented. '''

drf序列响应速度慢(-反序列化器使用)(2)

怎么保存就报错了??

这是因为我们的序列化器继承自Serializer,当调用序列化器的save方法的时候,我们需要自己实现序列器中的create方法来实现数据的入库操作。

from rest_framework import serializers class ColumnSerializer(serializers.Serializer): id = serializers.IntegerField() name = serializers.CharField(max_length=20, label='栏目') link_url = serializers.CharField(label= '链接') index = serializers.IntegerField(label='位置') def validate_name(self, value): # value就是字段对应的值 # 名称包含MD报错 if 'MD' in value: raise serializers.ValidationError('带有敏感词汇') return value # 多个字段验证 def validate(self, data): id = data.get('id') index = data.get('index') if id > index: raise serializers.ValidationError('id不能大于索引位置') return data def create(self, validated_data): ''' 如果我们的序列化器是继承自Serialzier 当调用序列化器的save方法的时候,会触发调用序列化器的create方法,我们需要自己实现序列化器中的create方法 validated_data: 验证没有问题的数据 如果我们的data 经过我们的层层验证,美誉问题,则 validated_data = data ''' # column = Column.objects.create() print(validated_data) column = Column.objects.create(**validated_data) return column

这样,当serializer.save()时,其实就是调用我们定义的create,也就返回一个column对象。

drf序列响应速度慢(-反序列化器使用)(3)

我们再看下,不经过验证直接保存,看看效果,如何:

from article.serializers import ColumnSerializer data = { 'id': 8, 'name': '项目案例', 'link_url': '/columns/8/', 'index': 8, } # 2.创建序列化器,将字典数据给序列化器 # instance用于序列化(对象转换为字典) # data 用于反序列化(字典转换为对象) serializer = ColumnSerializer(data=data) # 3. 验证数据 # serializer.is_valid(raise_exception=True) # 获取验证成功后的字段数据 print(serializer.validated_data()) # 4. 保存 serializer.save()

不经过校验,就会报错:

drf序列响应速度慢(-反序列化器使用)(4)

故需要经过校验通过后,才能进行保存。

更新数据

我们再来更新(修改)数据

from article.serializers import ColumnSerializer from article.models import Column data = { #'id': 8, 'name': '项目案例2', 'link_url': '/columns/82/', 'index': 8, } column = Column.objects.get(id=8) # 2.创建序列化器,将字典数据给序列化器 # instance用于序列化(对象转换为字典) # data 用于反序列化(字典转换为对象) serializer = ColumnSerializer(instance=column ,data=data) # 3. 验证数据 serializer.is_valid(raise_exception=True) # 4. 保存 serializer.save() ''' Traceback (most recent call last): File "<console>", line 1, in <module> File "/root/.virtualenvs/qmblog_env/lib/python3.7/site-packages/rest_framework/serializers.py", line 200, in save self.instance = self.update(self.instance, validated_data) File "/root/.virtualenvs/qmblog_env/lib/python3.7/site-packages/rest_framework/serializers.py", line 167, in update raise NotImplementedError('`update()` must be implemented.') NotImplementedError: `update()` must be implemented. '''

drf序列响应速度慢(-反序列化器使用)(5)

又是错错!!!

drf序列响应速度慢(-反序列化器使用)(6)

当我们的序列化器继承自Serializer,既传入了instance又传递了data,这个时候,系统会认为我们在进行更新(修改)数据,当我们调用序列化器的save方法的时候,会触发update方法。

drf序列响应速度慢(-反序列化器使用)(7)

而当前序列化器中没有实现这个update方法,我们需要自己来实现。

class ColumnSerializer(serializers.Serializer): id = serializers.IntegerField(read_only=True) name = serializers.CharField(max_length=20, label='栏目') link_url = serializers.CharField(label= '链接') index = serializers.IntegerField(label='位置') def validate_name(self, value): # value就是字段对应的值 # 名称包含MD报错 if 'MD' in value: raise serializers.ValidationError('带有敏感词汇') return value # 多个字段验证 # def validate(self, data): # id = data.get('id') # index = data.get('index') # if id > index: # raise serializers.ValidationError('id不能大于索引位置') # return data def create(self, validated_data): ''' 如果我们的序列化器是继承自Serialzier 当调用序列化器的save方法的时候,会触发调用序列化器的create方法,我们需要自己实现序列化器中的create方法 validated_data: 验证没有问题的数据 如果我们的data 经过我们的层层验证,美誉问题,则 validated_data = data ''' column = Column.objects.create(**validated_data) return column def update(self, instance, validated_data): ''' instance:序列化器创建的时候,传递的对象 validated_data:序列化器创建的时候,验证没有问题的数据 ''' instance.name = validated_data.get('name', instance.name) instance.link_url = validated_data.get('link_url', instance.link_url) instance.index = validated_data.get('index', instance.index) instance.save() return instance #return super().update(instance, validated_data) # views.py from article.serializers import ColumnSerializer from article.models import Column data = { #'id': 8, 'name': '项目案例2', 'link_url': '/columns/82/', 'index': 8, } column = Column.objects.get(id=8) # 2.创建序列化器,将字典数据给序列化器 # instance用于序列化(对象转换为字典) # data 用于反序列化(字典转换为对象) serializer = ColumnSerializer(instance=column ,data=data) # 3. 验证数据 serializer.is_valid(raise_exception=True) # 4. 保存 serializer.save() # <Column: 项目案例2> # 5.会获取新字典数据 serializer.data # {'id': 8, 'name': '项目案例2', 'link_url': '/columns/82/', 'index': 8}

drf序列响应速度慢(-反序列化器使用)(8)

上面,我们将id设置为read_only=True只用于序列化,那是因为反序列化时,我们已经通过id找到了实例对象(id一般不可变的),只需要修改其他字段值就可以了。

,