DRF 序列化组件单增
目录
自定义序列化(矬)
通常我们后端需要将数据查出来,并返回给需要数据的人,那么就产生了一个序列化的概念
我们可以自定义序列化并返回给客户端。
# 自定义序列化过程
class UserV1View(APIView):
def get(self,request,*args,**kwargs):
pk = kwargs.get('pk')
if pk:
# 单查
user_dict = models.User.objects.filter(pk=pk,is_delete=False).values('username','img','sex').first()
if not user_dict:
return Response({'status':1,'msg':'geterror'},status=400)
user_dict['img'] = '%s%s%s' % (settings.BASE_URL,settings.MEDIA_URL,user_dict.get('img'))
return Response({'status':0,'msg':'ok','results':user_dict})
else:
# 群查
user_query = models.User.objects.filter(is_delete=False).all().values('username','img','sex')
user_dict = list(user_query)
for i in user_dict:
i['img'] = '%s%s%s' % (settings.BASE_URL,settings.MEDIA_URL,i.get('img'))
return Response({'status': 0, 'msg': 'ok', 'results': user_dict})
但是这种方式太过于复杂,如果字段多了,不好做拓展,那么我们可以使用DRF提供给我们的方法
Serializer类(方式繁琐)
底层序列化类 UserSerializer
- 设置序列化字段,并且字段和类型必须和model模型类中一致
- 如果不参与序列化的字段不写在 UserSerializer 类中
- 自定义序列化字段,使用 serializers.SerializerMethodField()
api/serializers:
from rest_framework import serializers
from django.conf import settings
from api import models
# 序列化:将数据从后端传给前端
class UserSerializer(serializers.Serializer):
# 序列化字段
username = serializers.CharField()
# 自定义序列化字段
gender = serializers.SerializerMethodField()
# self:当前对象,obj参与序列化的models模型类对象
def get_gender(self,obj):
# 获取性别的映射
return obj.get_sex_display()
icon = serializers.SerializerMethodField()
def get_icon(self,obj):
return '%s%s%s' % (settings.BASE_URL, settings.MEDIA_URL,obj.img)
视图序列化步骤
视图CBV方式序列化过程:
- 视图通过ORM操作得到数据对象
- 将数据交给定义的序列化类UserSerializer序列化成返回给前台的数据
- 返回序列化的数据给前台
# DRF 的 Serializer 序列化过程
from api import serializers
class UserV2View(APIView):
def get(self,request,*args,**kwargs):
pk = kwargs.get('pk')
if pk:
# 单查
user_obj = models.User.objects.filter(pk=pk, is_delete=False).first()
if not user_obj:
return Response({'status': 1, 'msg': 'geterror'}, status=400)
user_dict = serializers.UserSerializer(user_obj,many=False).data
return Response({'status': 0, 'msg': 'ok', 'results': user_dict})
else:
# 群查
user_query = models.User.objects.filter(is_delete=False).all()
user_dict = serializers.UserSerializer(user_query,many=True).data
return Response({'status': 0, 'msg': 'ok', 'results': user_dict})
底层反序列化类 UserCreatSerializer
- 设置校验的字段,需与model模型类中的字段一致
- 自定义校验字段和设置的系统校验字段定义没有区别,但是不需要入库(这字段参与全局钩子校验)
- 所有字段可以设置对应局部钩子校验,校验成功返回value,失败抛异常
- 所有字段可以设置对应全局钩子校验,校验成功返回attrs,失败抛异常
- 重写create方法实现增入库,返回入库成功的对象
- 重写update方法实现改入库,返回入库成功的对象
# 反序列化:将数据从前端传给后端
class UserCreatSerializer(serializers.Serializer):
# models模型类的字段
username = serializers.CharField(min_length=3,max_length=8,error_messages={
'min_length':'用户名太短',
'max_length':'用户名太长',
'required':'用户名不能为空',
})
password = serializers.CharField(min_length=3,max_length=8)
re_password = serializers.CharField(min_length=3,max_length=8)
# 不写就不参与序列化,写上就必须参与序列化
# required=False表示可写可不写。前台不提供,走默认值,前提是定义默认值
sex = serializers.BooleanField(required=False)
# 局部钩子
# value:校验的字段数据
def validate_username(self,value):
if 'g' in value.lower():
raise serializers.ValidationError('名字中不能有g')
return value
# 全局钩子
# attrs:所有校验的数据
def validate(self, attrs):
password = attrs.get('password')
re_password = attrs.pop('re_password')
if password != re_password:
raise serializers.ValidationError({'re_password':'两次密码不一致'})
return attrs
# 在视图类中调用序列化类的save方法完成入库
# Serializer虽然有create方法,但具体没有实现,需要重写,因为不知道操作的是哪张表
def create(self, validated_data):
return models.User.objects.create(**validated_data)
# instance表示要被修改的对象,validated_data代表校验后修改instance的数据
def update(self, instance, validated_data):
validated_data.pop('username')
models.User.objects.filter(pk=instance.id).update(**validated_data)
return instance
视图反序列化步骤
- 从请求对象中获取前台携带的数据包参数
- 交给反序列化类完成反序列化(data=request.data),实际上是数据的校验
- 校验成功,借助反序列化类完成数据入库,返回前台入库成功的对象
def post(self,request,*args,**kwargs):
user_obj = serializers.UserCreatSerializer(data=request.data)
if user_obj.is_valid():
# 入库
user_obj = user_obj.save()
return Response({'status':0,'msg':'ok','results':serializers.UserSerializer(user_obj).data})
else:
return Response({'status':1,'msg':user_obj.errors})
ModelSerializer类(重点)
序列化与反序列化类 UserModelSerializer
- 序列化与反序列化类继承ModelSerializer类
- 在配置类Meta中绑定序列化与反序列化相关的models模型表
- fields配置,采用插拔式,设置所有参与序列化与反序列化字段
- extra_kwargs配置,划分字段类型,区分是序列化还是反序列化
- 序列化只读:read_only
- 反序列化只写:write_only
- 两者都可以可读可写:不写
- 自定义序列化字段,提倡在models模型类中使用@property实现,可插拔
- 自定义反序列化字段,同Serializer类设置字段即可,且可以在全局钩子校验,在extra_kwargs中设置的此字段类型无效,所以必须设置write_only=True
- 可以对字段设置局部钩子或者全局钩子,同Serializer类
- 不需要重写create和update方法,ModelSeializer类已提供
ModelSerializer 序列化类 比Serializer类高级,高级在我们不需要重写create和update方法,并且序列化和反序列化写在同一个类中,使用接口做序列化与反序列化区分就可以了。
# 序列化:将数据从后端传给前端
# 反序列化:将数据从前端传给后端
class UserModelSerializer(serializers.ModelSerializer):
# 自定义反序列化字段,需要声明write_only,在extra_kwargs中设置校验规则无效
re_password = serializers.CharField(min_length=3,max_length=8,write_only=True)
class Meta:
# 手动设置关联的model模型表
model = models.User
# 采用插拔式,设置所有参与序列化与反序列化字段
fields = ('username','gender','icon','sex','password','re_password')
extra_kwargs = {
# 不设置read_only或write_only,默认都参与。
'username':{
'min_length':3,
'max_length':8,
'error_messages':{
'min_length':'太短',
'max_length':'太长'
}
},
'icon':{'read_only':True},
'gender':{'read_only':True},
'password':{'write_only':True},
'sex':{'write_only':True}
}
# 钩子函数同Serializer类中实现方式一样
def validated_username(self,value):
if 'g' in value.lower():
raise serializers.ValidationError('名字中不能含有g')
return value
def validate(self, attrs):
password = attrs.get('password')
re_password = attrs.pop('re_password')
if password != re_password:
raise serializers.ValidationError({'re_password':'两次密码不一致'})
return attrs
# create和update方法不需要再重写,ModelSeializer类已提供。
视图序列化与反序列化步骤
视图序列化使用的序列化与反序列化类都是同一个定义的UserModelSerializer类
序列化:
- 操作ORM查询数据,返回数据对象
- 将返回的数据对象传入定义的UserModelSerializer类中,如果是单查,many=False,如果是群差,many=True,返回数据对象
- 将数据使用Response返回给前台
反序列化:
- 获取请求携带的数据包参数
- 传入定义的UserModelSerializer类中进行反序列化(data=request.data),实际上是数据的校验
- 校验成功,借助反序列化类完成数据入库,返回前台入库成功的对象
# DRF 的 ModelSerializer 序列化与反序列化过程
class UserV3View(APIView):
# 单查群查
def get(self,request,*args,**kwargs):
pk = kwargs.get('pk')
if pk:
# 单查
user_obj = models.User.objects.filter(pk=pk, is_delete=False).first()
if not user_obj:
return Response({'status': 1, 'msg': 'geterror'}, status=400)
user_dict = serializers.UserModelSerializer(user_obj,many=False).data
return Response({'status': 0, 'msg': 'ok', 'results': user_dict})
else:
# 群查
user_query = models.User.objects.filter(is_delete=False).all()
user_dict = serializers.UserModelSerializer(user_query,many=True).data
return Response({'status': 0, 'msg': 'ok', 'results': user_dict})
def post(self,request,*args,**kwargs):
user_obj = serializers.UserModelSerializer(data=request.data)
if user_obj.is_valid():
# 入库
user_obj = user_obj.save()
return Response({'status':0,'msg':'ok','results':serializers.UserModelSerializer(user_obj).data})
else:
return Response({'status':1,'msg':user_obj.errors})
models模型类中自定义序列化字段
# 自定义序列化字段(插拔式,官方提倡使用)
@property
def gender(self):
return self.get_sex_display()
@property
def icon(self):
from django.conf import settings
return '%s%s%s' % (settings.BASE_URL, settings.MEDIA_URL, self.img)