将字典转化为对象,就是反序列化。
在开发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.
'''
怎么保存就报错了??
这是因为我们的序列化器继承自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对象。
我们再看下,不经过验证直接保存,看看效果,如何:
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()
不经过校验,就会报错:
故需要经过校验通过后,才能进行保存。
更新数据我们再来更新(修改)数据
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.
'''
又是错错!!!
当我们的序列化器继承自Serializer,既传入了instance又传递了data,这个时候,系统会认为我们在进行更新(修改)数据,当我们调用序列化器的save方法的时候,会触发update方法。
而当前序列化器中没有实现这个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}
上面,我们将id设置为read_only=True只用于序列化,那是因为反序列化时,我们已经通过id找到了实例对象(id一般不可变的),只需要修改其他字段值就可以了。
,