序列化器的主要工作就是将前端传入后端的JSON数据转换为ORM模型映射
一个序列化器可以同时包含序列化和反序列化两个功能,想要用哪一个功能具体看你怎么调用
序列化: 将模型转换成json数据返回给前端,可以序列化一个模型类,也可以序列化多个模型类(many=True)
反序列化: 将json数据转换成模型,反序列化还可以用来进一步验证信息和保存数据
但验证的时候不一定保存数据入库,但是保存数据入库的时候一定要验证数据
序列化和反序列化的功能本质上都属于序列化器的功能
想要用到序列化器的序列化功能,就传入 ORM模型对象, 使之 模型转JSON
想要用到序列化器的反序列化功能就 传入 JSON数据,使之验证数据或者保存入库
定义一个ORM模型
from django.db import models
# 导入系统自带的用户模型类,在此基础上对起进行继承重写
from django.contrib.auth.models import AbstractUser
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer, BadData
from django.conf import settings
# 继承系统自带的用户模型类,在原有的基础上重新拓展新的字段
class User(AbstractUser):
"""我们自己的用户的模型类"""
# 给用户新增加手机号字段
mobile = models.CharField(max_length=11, verbose_name='手机号', unique=True)
# 给用户新增加邮箱验证状态属性
email_active = models.BooleanField(default=False, verbose_name='邮箱验证状态')
# 用户新增加默认的地址
default_address = models.ForeignKey('Address', related_name='users', null=True, blank=True,on_delete=models.SET_NULL, verbose_name='默认地址')
class Meta:
# 数据库中显示的表的名称
db_table = 'tb_users'
# 在admin站点中显示的名称
verbose_name = '用户'
verbose_name_plural = verbose_name
定义一个序列化器(这个序列化器是有模型的)
from rest_framework import serializers
# 在序列化器中导入用户的ORM模型
from .models import User
# 因为有ORM USER 模型,所以继承了 serializers.ModelSerializer
class UserDetailSerializer(serializers.ModelSerializer):
"""用户个人中心详细信息序列化器"""
class Meta: # 由于要序列化的有模型,因此需要添加 class Meta:
model = User # 明确用的是 User 模型
# 使用fields来明确字段,__all__表名包含所有字段,也可以写明具体哪些字段
fields = ('id', 'username', 'mobile', 'email', 'email_active')
在视图中使用序列化器(用到了序列化器的序列化功能,把ORM模型转换为JSON数据类型返回给前端)
# 导入登录用户的认证信息
from rest_framework.permissions import IsAuthenticated
class UserDetailView(RetrieveModelMixin, GenericAPIView):
permission_classes = [IsAuthenticated]
serializer_class = UserDetailSerializer
# 重写了get_object()方法
# get_object()方法是用来获取用户模型类对象的
# 但是前端返回的值只有加密之后的token
# request可以获取请求的用户对象,这是前面的知识点
# 所以重写get_object()方法,直接强制赋予用户模型类对象
def get_object(self):
return self.request.user
# 通过get方法把用户模型转换为json之后数据传回前端
def get(self, request):
# 调用RetrieveModelMixin类内部的retrieve()方法,该方法内部会调用GenericAPIView类内部的get_serializer()方法,来获得json数据
return self.retrieve(request)
############################################################################
最终把JSON数据类型返回给前端,前端接收到JSON数据进行页面展示
定义一个序列化器(这个序列化器没有模型)
from rest_framework import serializers
# 因为这个序列化器没有用到模型,所以继承自 serializers.Serializer
class BookInfoSerializer(serializers.Serializer):
"""图书数据序列化器"""
id = serializers.IntegerField(label='ID', read_only=True)
name = serializers.CharField(label='名称', max_length=20)
pub_date = serializers.DateField(label='发布日期', required=False)
readcount = serializers.IntegerField(label='阅读量', required=False)
commentcount = serializers.IntegerField(label='评论量', required=False)
image = serializers.ImageField(label='图片', required=False)
使用序列化器序列化一个模型对象
from book.models import BookInfo
# 查询出 id = 4 的时候的 模型对象
book = BookInfo.objects.get(id=4)
# 构造序列化器对象
from book.serializers import BookInfoSerializer
serializer = BookInfoSerializer(book)
# 通过 serializer.data 可以获取 序列化之后的 json 数据
serializer.data ---> {'is_delete': False, 'pub_date': '1987-11-11', 'readcount': 58}
使用序列化器序列化多个模型对象
from book.models import BookInfo
# 查询出所有的模型类对象(多个)
books = BookInfo.objects.all()
# 序列化多个的时候需要通过加上many=True
serializer = BookInfoSerializer(books, many=True)
serializer.data
[OrderedDict([('id', 3), ('name', '笑傲江湖'), ('pub_date', '1995-12-24'), ('readcount', 28)), OrderedDict([('id', 4), ('name', '雪山飞狐'), ('pub_date', '1987-11-11'), ('readcount', 58))]
反序列化主要有两个功能:
1.验证数据
2.保存数据入库
在获取反序列化的数据前,必须调用**is_valid()**方法进行验证,验证成功返回True,否则返回False。
验证失败,可以通过序列化器对象的errors属性获取错误信息,返回字典,包含了字段和字段的错误。
验证成功,可以通过序列化器对象的validated_data属性获取数据。
之前定义过的 BookInfoSerializer 序列化器
class BookInfoSerializer(serializers.Serializer):
"""图书数据序列化器"""
id = serializers.IntegerField(label='ID', read_only=True)
name = serializers.CharField(label='名称', max_length=20)
pub_date = serializers.DateField(label='发布日期', required=False)
readcount = serializers.IntegerField(label='阅读量', required=False)
commentcount = serializers.IntegerField(label='评论量', required=False)
image = serializers.ImageField(label='图片', required=False)
通过构造序列化器对象,并将要反序列化的数据传递给data构造参数,进而进行验证
错误
>>> from book.serializers import BookInfoSerializer
>>> data = {'pub_date':123}
>>> serializer = BookInfoSerializer(data=data)
>>> serializer.is_valid()
False
# 验证失败,可以通过序列化器对象的errors属性获取错误信息,返回字典,包含了字段和字段的错误
>>> serializer.errors
{'pub_date': [ErrorDetail(string='Date has wrong format. Use one of these formats instead: YYYY[-MM[-DD]].', code='invalid')], 'name': [ErrorDetail(string='This field is required.', code='required')]}
# 因为出现了错误,所以获取不到任何数据
>>> serializer.validated_data
{}
正确
>>> from book.serializers import BookInfoSerializer
>>> data = {'pub_date':'2010-1-1','name':'python高级'}
# 传入json数据进行验证
>>> serializer = BookInfoSerializer(data=data)
# 在获取反序列化的数据前,必须调用is_valid()方法进行验证,验证成功返回True,否则返回False。
# is_valid()方法还可以在验证失败时抛出异常serializers.ValidationError,可以通过传递raise_exception=True参数开启,REST framework接收到此异常,会向前端返回HTTP 400 Bad Request响应。
>>> serializer.is_valid()
True
>>> serializer.errors
{}
# 可以通过序列化器对象的validated_data属性获取数据(字典格式)
>>> serializer.validated_data
OrderedDict([('name', 'python高级'), ('pub_date', datetime.date(2010, 1, 1))])
如果创建序列化器对象的时候,没有传递instance实例,则调用save()方法的时候,create()被调用,相反,如果传递了instance实例,则调用save()方法的时候,update()被调用。
定义一个 ORM 模型
# 导入系统自带的用户模型类,在此基础上对起进行继承重写
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
"""我们自己的用户的模型类"""
# 给用户增加手机号字段
mobile = models.CharField(max_length=11, verbose_name='手机号', unique=True)
# 给用户增加邮箱验证状态属性
email_active = models.BooleanField(default=False, verbose_name='邮箱验证状态')
# 用户默认的地址
default_address = models.ForeignKey('Address', related_name='users', null=True, blank=True,on_delete=models.SET_NULL, verbose_name='默认地址')
class Meta:
# 数据库中显示的表的名称
db_table = 'tb_users'
# 在admin站点中显示的名称
verbose_name = '用户'
verbose_name_plural = verbose_name
定义一个序列化器,使用里面的校验和入库功能
# 因为需要进行校验入库的是数据是有模型的,所以需要继承自 ModelSerializer
class RegisterCreateSerializer(serializers.ModelSerializer):
# 用户再一次进行提交的时候有三个数据: 校验的密码,短信验证码,用户是否同意协议
# write_only 表明该字段仅用于反序列化输入,也就是说不需要入库,默认False
password2 = serializers.CharField(label='校验密码', allow_null=False, allow_blank=False, write_only=True)
sms_code = serializers.CharField(label='短信验证码', max_length=6,min_length=6, allow_null=False, allow_blank=False, write_only=True)
allow = serializers.CharField(label='是否同意协议', allow_null=False, allow_blank=False, write_only=True)
# 增加对token的字段,因为token是jwt自动生成的,我们只需要把他序列化成模型状态就好了,不需要反序列化进行校验
token = serializers.CharField(label='登录状态的token', read_only=True) # token不能被修改,也不需要反序列化校验,只需要入库就可以了
class Meta:
# model 指明参照哪个模型类
model = User
# 使用fields来明确字段,__all__表名包含所有字段
fields = ('id', 'username', 'password', 'mobile', 'password2', 'sms_code', 'allow', 'token')
# 使用extra_kwargs参数为ModelSerializer添加或修改原有的选项参数
extra_kwargs = {
'id': {'read_only': True},
'username': {
'min_length': 5,
'max_length': 20,
'error_messages': {
'min_length': '仅允许5-20个字符的用户名',
'max_length': '仅允许5-20个字符的用户名',
}
},
'password': {
'write_only': True,
'min_length': 8,
'max_length': 20,
'error_messages': {
'min_length': '仅允许8-20个字符的密码',
'max_length': '仅允许8-20个字符的密码',
}
}
}
# 开始进行校验
# 先进行单字段校验(手机号,是否同意协议)
# 进行手机号校验
def validate_mobile(self,value):
if not re.match(r'1[345789]\d{9}', value):
raise serializers.ValidationError('手机号格式不正确')
return value
# 检查用户是否同意协议
def validate_allow(self, value):
# 在前端部分,我们已经把同意的格式从二进制转换为字符串
if value != 'true':
raise serializers.ValidationError('您尚未同意本协议')
return value
# 多个字段校验(手机验证码的校验,两次密码输入的是否一致)
def validate(self, attrs):
# 现获取出来两次输入的密码进行比较
password = attrs.get('password')
password2 = attrs.get('password2')
if password != password2:
raise serializers.ValidationError('两次密码输入不一致')
# 获取用户的手机号和用户输入短信验证码,通过手机号取出redis中验证码,然后二者进行比较
mobile = attrs.get('mobile')
sms_code = attrs.get('sms_code')
redis_conn = get_redis_connection('code')
redis_sms_code = redis_conn.get('sms_%s' % mobile)
# 从redis中获取出来的手机验证码有过期的可能性,需要校验是否为空
if redis_sms_code is None:
raise serializers.ValidationError('验证码已经过期')
# 从redis中获取到的短信验证码和用户输入的短信验码进行比较
if sms_code != redis_sms_code.decode():
raise serializers.ValidationError('验证码输入有误')
# 把校验之后的结返回回去
return attrs
###################################################################################
## 若数据校验没有问题的话,则create 中的 validated_data 和 单字段校验中的attrs 是一个数据 ##
###################################################################################
# 重写入库的方法
def create(self, validated_data):
# 删除多余字段 ---> 因为在创建用户模型类的时候下面的三个字段并没有创建,
# 因为这个三个字段只是为了校验用户注册并不包含用户的具体信息
# 但是却需要对这三个字段进行校验
# 用户的模型类里面不包含这三个字段,这三个字段却要辅助用户模型类校验,因此在序列化器的最上面
# 添加字段,但是在入库的时候却删除这三个字段
del validated_data['password2']
del validated_data['sms_code']
del validated_data['allow']
# 调用这个方法会返回一个用户模型对象
user = super().create(validated_data)
# 对密码进行加密
user.set_password(validated_data['password'])
# 保存入库
user.save()
# 我们在创建了用户之后,需要通过后端jwt产生token,传递给前端
# 这样用户登录的时候就可以携带token了
from rest_framework.settings import api_settings
# 获取两方法
# JWT_PAYLOAD_HANDLER可以从api_settings的源代码里面获取
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
# 调用这两个方法,把用户的信息传递给方法之中,然后通过加密产生了token
payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)
# 把token消息传递给token,我们在进行序列化的时候把token传递给浏览器
user.token = token
return user
定义一个序列化器
# 因为就验证一个字段,也没有模型,所以继承serializers.Serializer
# 这个序列化器主要对用户浏览商品时的商品编号 sku_id 进行验证并校验存入redis
class UserHistorySerializer(serializers.Serializer):
sku_id = serializers.CharField(label='商品id', required=True)
# 对单字段进行验证
def validate_sku_id(self, value):
try:
SKU.objects.get(pk=value)
except SKU.DoesNotExist:
raise serializers.ValidationError('商品不存在')
return value
# 去重写create()方法,把数据保存在redis中,不去重写的话,就要保存mysql中了,但是我们没有定义mysql的模型
def create(self, validated_data):
# 通过context把放入的user给取出来
user = self.context.get('request').user
sku_id = validated_data.get('sku_id')
# 连接redis
redis_conn = get_redis_connection('history')
# LREM key count value---> count = 0 : 移除表中所有与 value 相等的值
# 也就是说移除所有与sku_id相同的值
redis_conn.lrem('history_%s' % user.id, 0, sku_id)
# 先移除旧的数据,然后添加新的数据
redis_conn.lpush('history_%s' % user.id, sku_id)
# 只保留5条记录(redis不能保留太多的记录)
# LTRIM key start stop 对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除。
redis_conn.ltrim('history_%s' % user.id, 0, 4)
return validated_data
定义一个把展示商品的ORM模型转换为JSON的序列化器
# 因此涉及到展示商品,而展示商品是有模型的,所以继承ModelSerializer
class SKUSerializer(serializers.ModelSerializer):
class Meta:
model = SKU
fields = ('id', 'name', 'price', 'default_image_url', 'comments')
定义一个视图来使用 序列化器
# 导入序列化器
from .serializer import UserHistorySerializer
from goods.models import SKU
# 记录用户浏览历史的类
class UserHistoryView(GenericAPIView):
# 登录用户才能访问
permission_classes = [IsAuthenticated]
# 添加序列化器,对信息尽心校验
serializer_class = UserHistorySerializer
def post(self, request):
# 在构造序列化器对象时,可以通过context参数添加额外的数据给序列化器
# request里面有user,把request传递给序列化器,可以在序列化器中通过request
# 取出user,然后作为redis的唯一辨识度的键来使用
serializer = self.get_serializer(data=request.data, context={'request': request})
serializer.is_valid(raise_exception=True)
# 向序列化器中传入的data而没有传入instance,说明serializer.save()的时候,只会调用create()方法,而不会调用update()方法
serializer.save()
return Response({'message': 'OK'})
# 查看用户浏览历史的方法
def get(self, request):
# 获取出当前查看浏览历史的对象
user = request.user
# 连接redis
redis_conn = get_redis_connection('history')
# 通过用的id把浏览记录给获取出来(获取出来的是列表数据)
ids = redis_conn.lrange('history_%s' % user.id, 0, 5)
skus = [] # skus列表中存储着多个模型类对象
for id in ids:
sku = SKU.objects.get(pk=id)
skus.append(sku)
# 用到了序列化器的序列化功能,把模型转换成json
serializer = SKUSerializer(skus, many=True)
# 把模型转换成的json返回给前端
return Response(serializer.data)
如果我们想要使用序列化器对应的是Django的模型类,DRF为我们提供了ModelSerializer模型类序列化器来帮助我们快速创建一个Serializer类。
ModelSerializer与常规的Serializer相同,但提供了:
比如我们创建一个BookInfoSerializer
class BookInfoSerializer(serializers.ModelSerializer):
class Meta:
model = BookInfo
fields = '__all__'
我们可以在python manage.py shell中查看自动生成的BookInfoSerializer的具体实现
>>> from book.serializers import BookInfoSerializer
>>> serializer = BookInfoSerializer()
>>> serializer
BookInfoSerializer():
id = IntegerField(label='ID', read_only=True)
name = CharField(label='名称', max_length=20)
pub_date = DateField(label='发布日期', required=True)
readcount = IntegerField(label='阅读量', required=True)
commentcount = IntegerField(label='评论量', required=True)
image = ImageField(label='图片', required=False)
peopleinfo_set = PrimaryKeyRelatedField(many=True, read_only=True)
__all__
表名包含所有字段,也可以写明具体哪些字段,如class BookInfoSerializer(serializers.ModelSerializer):
class Meta:
model = BookInfo
fields = '__all__'
class BookInfoSerializer(serializers.ModelSerializer):
class Meta:
model = BookInfo
exclude = ('image',)
class PeopleInfoSerializer(serializers.ModelSerializer):
class Meta:
model = PeopleInfo
fields = '__all__'
depth = 1
形成的序列化器如下:
>>> from book.serializers import PeopleInfoSerializer
>>> serializer = PeopleInfoSerializer()
>>> serializer
PeopleInfoSerializer():
id = IntegerField(label='ID', read_only=True)
name = CharField(label='名称', max_length=20)
gender = ChoiceField(choices=((0, 'male'), (1, 'female')), label='性别', required=False, validators=[<django.core.vidators.MinValueValidator object>, <django.core.validators.MaxValueValidator object>])
description = CharField(allow_null=True, label='描述信息', max_length=200, required=False)
is_delete = BooleanField(label='逻辑删除', required=False)
book = NestedSerializer(read_only=True):
id = IntegerField(label='ID', read_only=True)
name = CharField(label='名称', max_length=20)
pub_date = DateField(label='发布日期')
readcount = IntegerField(label='阅读量', max_value=2147483647, min_value=-2147483648, required=False)
commentcount = IntegerField(label='评论量', max_value=2147483647, min_value=-2147483648, required=False)
is_delete = BooleanField(label='逻辑删除', required=False)
image = ImageField(allow_null=True, label='图片', max_length=100, required=False)
class BookInfoSerializer(serializers.ModelSerializer):
class Meta:
model = BookInfo
fields = ('id','name', 'readcount', 'commentcount')
可以通过read_only_fields指明只读字段,即仅用于序列化输出的字段
class BookInfoSerializer(serializers.ModelSerializer):
class Meta:
model = BookInfo
fields = ('id','name', 'readcount', 'commentcount')
read_only_fields = ('id', 'readcount', 'commentcount')
我们可以使用extra_kwargs参数为ModelSerializer添加或修改原有的选项参数
class BookInfoSerializer(serializers.ModelSerializer):
class Meta:
model = BookInfo
fields = ('id','name', 'readcount', 'commentcount')
read_only_fields = ('id', 'readcount', 'commentcount')
extra_kwargs = {
'readcount': {'min_value': 0, 'required': True},
'commentcount': {'max_value': 0, 'required': True},
}
>>> from book.serializers import BookInfoSerializer
>>> serializer = BookInfoSerializer()
>>> serializer
BookInfoSerializer():
id = IntegerField(label='ID', read_only=True)
name = CharField(label='名称', max_length=20)
readcount = IntegerField(label='阅读量', min_value=0, read_only=True)
commentcount = IntegerField(label='评论量', max_value=0, read_only=True)