DRF序列化器

DRF序列化

序列化和反序列化

本质

序列化:后端把对象数据(Model)转化成字符串形式

反序列化:把字符串形式数据转化为对象数据(Model)

注意

一般字符串数据也有约束,因为字符串涉及多个端,多种语言的交互,为此提供了json形式的字符串格式以支持多语言都可以转化成自己的对象数据,便于操作

序列化模块

序列化类总览

主要分三个类,他们的关系可直接看继承了解

from rest_framework.serializers import Serializer,ModelSerializer,ListSerializer

class Serializer(BaseSerializer, metaclass=SerializerMetaclass):
#偏底层,开发效率不高
Serializer

class ModelSerializer(Serializer):
#开发运用阶段才有的序列化方式,开发效率高
ModelSerializer

#完成群增,群改接口的辅助序列化类
class ListSerializer(BaseSerializer):
ListSerializer

Serializer使用

注意

数据存放的位置是data还是默认(区分序列化和反序列化)

many=true还是false,区分多数据和单个数据

Serializer序列化

概括
1)ORM操作数据库拿到资源数据
2)格式化(序列化)成能返回给前台的数据
3)返回格式化后的数据

1.新建一个类serializers.py

#配置可序列化的字段
#配置数据校验

from rest_framework import serializers
class UserSerializer(serializers.Serializer):

    #   如果要参与序列化,名字一定要与model的属性同名(他是通过反射与modles关联的)
    #   如果不参与序列化的model属性,在序列化类中不做声明

    name = serializers.CharField()
    age = serializers.IntegerField()
    #其实这里面的参数是没有用的
    height = serializers.DecimalField(max_digits=5, decimal_places=2)
    #如果modles中出现了choice这种方式,就可以使用自定义的序列化字段,如sex可以使用gender来获
    值并序列化
    # sex = serializers.IntegerField()

    #   方法的名字:固定为 get_属性名,
    #   方法的参数:序列化对象,序列化的model对象
    #   注意字段不要重名,否则只会序列化后面的字段
	
	#自定义序列化字段使用SerializerMethodField这个方法
    gender = serializers.SerializerMethodField()
    def get_gender(self, obj):
        return obj.get_sex_display()

2使用

注意many参数,它是判断序列化单个对象和对象列表的核心参数

序列化出来的对象,是序列化对象,结果存在它的data里面

from . import serializers

#单查
user_obj = models.User.objects.filter(pk=pk).first()
user_ser = serializers.UserSerializer(user_obj,many=False)
user_data = user_ser.data

# 群查
user_query = models.User.objects.all()
user_list_data = serializers.UserSerializer(user_query, many=True).data

Serializer反序列化

概括
1)从请求对象中拿到前台的数据
2)校验前台数据是否合法
3)反序列化成后台Model对象与数据库交互

和django的form极为相识,但是还对数据库操作进行了封装

1.新建一个类serializers.py

注意

如果不是特殊的错误,如果不想自定义,又嫌弃英文,可以配置一下国际化,就不用专门为显示来配置所有的字段的错误的中文显示

先验证普通字段,在验证局部钩子,在全局钩子

from . import models
class UserDeserializer(serializers.Serializer):

    #只需要配置想要反序列化的字段,虽然反序列化不用和modles字段名对应,但是存入数据库还是要与modles的字段名字相同,那么就直接写的一样省的麻烦
    #虽然只需要配置想要的字段,但是还是需要注意,那些字段是进行数据库操作的必需字段(比如有默认值  的字段可以不用写)
    name = serializers.CharField(min_length=3, max_length=64, error_messages={
    	#可以使用国际化
        'required': '姓名必填',
        'min_length': '太短',
    })
    pwd = serializers.CharField(min_length=3, max_length=64)

    # 系统可选的反序列化字段:没有提供不进行校验(数据库中有默认值或可以为空),提供了就进行校验
    # required=False不必须但是传值了就进行校验
    age = serializers.IntegerField(min_value=0, max_value=150, required=False)

    # 自定义反序列化字段:一定参与校验,且要在校验过程中,将其从入库的数据中取出,剩余与model对应的数据才会入库(因为modles没有这个参数)
    re_pwd = serializers.CharField(min_length=3, max_length=64)

    # 自定义校验规则:局部钩子,全局钩子
    # 局部钩子:validate_字段名(self, 字段值)
    # 规则:成功返回value,失败抛异常
    def validate_aaa(self, value):
        if 'g' in value.lower():
            raise serializers.ValidationError('名字中不能有g')
        return value

    # 全局钩子:validate(self, 所有校验的数据字典)
    # 可以在全局钩子对所有参数的调整
    # 规则:成功返回attrs,失败抛异常
    def validate(self, attrs):
        # 取出联合校验的字段们:需要入库的值需要拿到值,不需要入库的需要从校验字段中取出
        pwd = attrs.get('pwd')
        re_pwd = attrs.pop('re_pwd')
        if pwd != re_pwd:
            raise serializers.ValidationError({'re_pwd': '两次密码不一致'})
        return attrs

    # create重写,完成入库
    def create(self, validated_data):
        return models.User.objects.create(**validated_data)

2.使用

查看save的源码,其实它最后调用的是下面的代码,并没有数据库功能,所以要自己处理数据库的操作

    def create(self, validated_data):
        return [
            self.child.create(attrs) for attrs in validated_data
        ]
    # 单增
    def post(self, request, *args, **kwargs):
        request_data = request.data
		
		#注意使用data,数据放在data才是反序列化(这是序列化和反序列化的区别)
        user_ser = serializers.UserModelSerializer(data=request_data)

        result = user_ser.is_valid()
        if result:
            user_obj = user_ser.save()
            return Response({
                'status': 0,
                'msg': 'ok',
                'results': serializers.UserModelSerializer(user_obj).data
            })
        else:
            # 校验失败,返回错误信息
            return Response({
                'status': 1,
                'msg': user_ser.errors
            }, status=status.HTTP_400_BAD_REQUEST)

ModelSerializer

原理

ModelSerializer是基于以上Serializer进行优化的,有更方便的写法和优化

配置

注意******************

fields

1.序列化和反序列化共用了,这些字段名,反序列化不再像上面这样可以随意写了,它也用到了反射,比方它会自动获取字段是不是必需的,必需的字段不填写值就会报错(上面的反序列化是没有整合数据库的那一块的,这里整合了)

2.序列化默认是转化field里面的所有字段,反序列化默认转化,filed里面有,且可以和modles对应的字段(比如确认密码,和gender性别转化这种不会转化),但是也是支持自定义字段的,比如re_pwd字段必须设置write_only=True

write_only和read_only

排除以上的第二点,所有字段都是read加write的,除非自己添加了修改了,write_only为只参与反序列化,read_only只参与序列化

关于choice的字段数据

这样写了之后,可以直接在UserModelSerializer的fields里面填写gender就行了

# 自定义插拔序列化字段:替换了在Serializer类中自定义的序列化字段(SerializerMethodField)
# 自定义插拔序列化字段一定不参与反序列化过程
@property
def gender(self):
    return self.get_sex_display()

关于required

由于反射,它自带默认值,如果models为必须就为true,如果有默认值,或者为null,默认为false

from rest_framework.serializers import ModelSerializer
from . import models
# 整合序列化与反序列化
class UserModelSerializer(ModelSerializer):
    # 将序列化类与Model类进行绑定
    # 设置序列化与反序列化所有字段(并划分序列化字段与反序列化字段)
    # 设置反序列化的局部与全局钩子

    # 自定义反序列化字段,校验规则只能在声明自定义反序列化字段时设置,且一定是write_only
    re_pwd = serializers.CharField(min_length=3, max_length=64, write_only=True)

    class Meta:
        model = models.User
        fields = ['name', 'age', 'height', 'gender', 'pwd', 're_pwd']
        extra_kwargs = {
            'name': {
                'required': True,
                'min_length': 3,
                'error_messages': {
                    'min_length': '太短'
                }
            },
            'age': {
                'required': True,  # 数据库有默认值或可以为空字段,required默认为False
                'min_value': 0
            },
            'pwd': {
                'required': True,
                'write_only': True,  # 只参与反序列化
            },
            'gender': {
                'read_only': True,  # 只参与序列化
            },
        }

    def validate_name(self, value):
        if 'g' in value.lower():
            raise serializers.ValidationError('名字中不能有g')
        return value

    def validate(self, attrs):
        pwd = attrs.get('pwd')
        re_pwd = attrs.pop('re_pwd')
        if pwd != re_pwd:
            raise serializers.ValidationError({'re_pwd': '两次密码不一致'})
        return attrs

    # ModelSerializer已经帮我们重写了入库方法

使用

关于业务逻辑这一块和上面的变化很小

注意******************

关于save(),ModelSerializer已经重写,不需要自己重写了

create不用我们重写了,而且ModelSerializer的create很强大,支持多表关系

关于钩子函数还是和上面一样,没有变化

is_valid()

如果raise_exception设置为True

校验失败,直接抛异常,反馈异常信息给前台,只要校验通过,代码才会往下执行

如果想自己处理返回信息就使用默认值

def is_valid(self, raise_exception=False):
from rest_framework.views import APIView
from rest_framework.response import Response
from . import models, serializers
from rest_framework import status
class UserV2APIView(APIView):
    # 单查群查
    def get(self, request, *args, **kwargs):
        pk = kwargs.get('pk')
        if pk:
            user_obj = models.User.objects.filter(pk=pk).first()
            if not user_obj:
                return Response({
                    'status': 1,
                    'msg': '单查 error'
                })

            # 完成序列化
            user_data = serializers.UserModelSerializer(user_obj, many=False).data

            return Response({
                'status': 0,
                'msg': '单查 ok',
                'results': user_data
            })

        # 群查
        user_query = models.User.objects.all()
        # 完成序列化
        user_list_data = serializers.UserModelSerializer(user_query, many=True).data
        return Response({
            'status': 0,
            'msg': '群查 ok',
            'results': user_list_data
        })

    # 单增
    def post(self, request, *args, **kwargs):
        request_data = request.data

        user_ser = serializers.UserModelSerializer(data=request_data)

       
        result = user_ser.is_valid()
        if result:
            user_obj = user_ser.save()
            return Response({
                'status': 0,
                'msg': 'ok',
                'results': serializers.UserModelSerializer(user_obj).data
            })
        else:
            # 校验失败,返回错误信息
            return Response({
                'status': 1,
                'msg': user_ser.errors
            }, status=status.HTTP_400_BAD_REQUEST)

参考链接

https://www.cnblogs.com/huanghongzheng/p/11373700.html

https://www.cnblogs.com/xiaoyuanqujing/articles/11670344.html

posted @ 2019-11-21 00:39  zx125  阅读(346)  评论(0编辑  收藏  举报