• Django(45)drf序列化类的使用(Serializer)


    前言

    上一篇文章我们讲述了序列化,这篇就带大家一起来实现以下序列化
     

    Serializer

    我们使用序列化类Serializer,我们来看下源码结构,这里推荐使用pycharm左边导航栏的Structure,可以清晰的看到一个文件的结构,如下图

    我们会发现Serializer继承自BaseSerializerSerializerMetaclass,但是Serializer类中又没有create方法和update方法,所以我们使用的时候必须自己手动定义这2个方法
     

    准备工作

    1.新建一个项目drf_demo,在项目中新建一个appdrf_app,在app中新建一个文件urls.py,项目结构如下

    2.在models.py文件中写入如下代码

    class Student(models.Model):
    
        SEX_CHOICES = (
            (1,'男'),
            (2, '女')
        )
    
        name = models.CharField(max_length=20, verbose_name='姓名')
        age = models.IntegerField(null=True, blank=True, verbose_name='年龄')
        sex = models.IntegerField(choices=SEX_CHOICES, default=1, verbose_name='性别')
    
        class Meta:
            db_table = "student"
    

    3.在drf_demo.urls.pydrf_app.urls.py中分别写入如下代码

    # drf_demo.urls.py
    urlpatterns = [
        path('drf/', include('drf_app.urls')),
    ]
    
    # drf_app.urls.py
    app_name = "drf_app"
    urlpatterns = [
        path('student/', views.student),
    ]
    

    4.在settings.py文件的MIDDLEWARE中注释掉django.middleware.csrf.CsrfViewMiddleware,并在INSTALLED_APPS中加入2个app

    'rest_framework',
    'drf_app'
    

    5.在命令行输入以下命令,将orm对象映射到数据库

    python manage makemigrations
    python manage migrate
    

    6.写序列化类一般我们都在app项目中新建serializers.py文件,接下来可以正式编写序列化类了
     

    序列化类编写

    # Serializer的构造函数的参数:
    # 1. instance:需要传递一个orm对象,或者是一个queryset对象,用来将orm转成json
    # 2. data:把需要验证的数据传递给data,用来验证这些数据是不是符合要求
    # 3. many:如果instance是一个queryset对象,那么就需要设置为True,否则为False
    
    class StudentSerializer(serializers.Serializer):
        # 序列化提供给前台的字段个数由后台决定,可以少提供
        # 但是提供的数据库对应的字段,名字一定要与数据库字段相同
    
        id = serializers.IntegerField(read_only=True)
        name = serializers.CharField(required=True, max_length=200)
        sex = serializers.IntegerField(required=True)
        age = serializers.IntegerField(required=True)
    
        def create(self, validated_data):
            """
            根据提供的验证过的数据创建并返回一个新的`Student`实例
            """
            return Student.objects.create(**validated_data)
    
        def update(self, instance, validated_data):
            """
            根据提供的验证过的数据更新和返回一个已经存在的`Student`实例。
            """
            instance.name = validated_data.get('name', instance.name)
            instance.age = validated_data.get('name', instance.age)
            instance.sex = validated_data.get('name', instance.sex)
            instance.save()
            return instance
    
        # 局部钩子 validate_要校验的字段名(self, 当前要校验字段的值)
        def validate_name(self, value):
            if 'j' in value.lower():
                raise exceptions.ValidationError("名字非法")
            return value
    
        def validate_sex(self, value):
            if not isinstance(value, int):
                raise exceptions.ValidationError("只能输入int类型")
            if value != 1 and value != 2 :
                raise exceptions.ValidationError("只能输入男和女")
            return value
    
        # 全局钩子 validate(self, 系统与局部钩子校验通过的所有数据)
        def validate(self, attrs):
            age = attrs.get('age')
            sex = attrs.get('sex')
            if age < 22 and sex == 1:
                raise exceptions.ValidationError({"age&sex": "男的必须22周岁以上才能结婚"})
            return attrs
    

    我们上面代码首先定义了序列化的字段,字段中的参数都继承自Field类,参数如下

    def __init__(self, read_only=False, write_only=False,
                 required=None, default=empty, initial=empty, source=None,
                 label=None, help_text=None, style=None,
                 error_messages=None, validators=None, allow_null=False):
    
    • read_only:当为True时表示这个字段只能读,只有在返回数据的时候会使用。
    • write_only:当为True时表示这个字段只能写,只有在新增数据或者更新数据的时候会用到。比如我们的账号密码,只允许用户提交,后端是不返回密码给前台的
    • required:当为True时表示这个字段必填,不填状态码会返回400
    • default:默认值,没什么好说的
    • allow_null:当为True时,允许该字段的值为空

      之后我们又定义了局部钩子,校验特殊的字段,比如需求规定,用户的性别只能输入男和女,此时你就可以定义一个钩子,当然drf自动帮我们做了一些校验,比如需要的字段是int类型,你输入string类型,会自动触发系统的error,不需要我们额外定义,后面我们会进行测试

      接下来我们又定义了一个全局的钩子,意思就是针对整个数据进行校验,最适合的场景比如密码重复输入,一般我们注册的时候,需要输入2次密码,第二次用来确认,这个场景就适合用全局钩子
     
    编写完serializers后,我们最后一步,编写视图函数,如下:

    def student(request):
        # 获取所有的学生
        if request.method == "GET":
            # 创建一个queryset对象
            stu = Student.objects.all()
            # 将对象序列化为dict 
            stu_data = StudentSerializer(instance=stu, many=True).data
            return JsonResponse(status=200, data=stu_data, safe=False)
        elif request.method == "POST":
            data = JSONParser().parse(request)
            serializer = StudentSerializer(data=data)
            # 校验字段是否符合规范
            if serializer.is_valid():
                # 符合则保存到数据库
                serializer.save()
                return JsonResponse(data=serializer.data, status=200)
            return JsonResponse(serializer.errors, status=400)
    

    测试

    测试分为GET请求和POST请求
     

    GET请求

    我们打开接口测试工具postman或者apifox,这里以apifox为例,输入127.0.0.1:8000/drf/student/,得到了以下结果

    [
        {
            "id": 1,
            "name": "jkc",
            "sex": 1,
            "age": 18
        },
        {
            "id": 2,
            "name": "mary",
            "sex": 2,
            "age": 20
        }
    ]
    

    说明序列化成功,成功地将数据库的数据通过json的格式返回给了前台
     

    POST请求

    同样打开接口工具,输入127.0.0.1:8000/drf/student/,在body中选择json格式,输入如下数据

    {
        "name": "aaaa",
        "sex": 2,
        "age": 18
    }
    

    运行结果如下:

    {
        "id": 13,
        "name": "aaaa",
        "sex": 2,
        "age": 18
    }
    

    说明我们反序列化也成功了,小伙伴们自己实践时可以查看数据库,会多了一条这样的数据
    接下来我们是否能触发钩子函数
     

    测试validate_name钩子

    输入测试数据

    {
        "name": "jjj",
        "sex": 2,
        "age": 18
    }
    

    返回结果如下:

    {
        "name": [
            "名字非法"
        ]
    }
    

    测试validate_sex钩子

    输入测试数据

    {
        "name": "kkk",
        "sex": 3,
        "age": 18
    }
    

    返回结果如下:

    {
        "sex": [
            "只能输入男和女"
        ]
    }
    

    测试默认的输入类型错误

    输入测试数据

    {
        "name": "kkk",
        "sex": "???",
        "age": 18
    }
    

    返回结果如下:

    {
        "sex": [
            "请填写合法的整数值。"
        ]
    }
    

    测试默认的必填项不填

    输入测试数据

    {
        "name": "kkk"
    }
    

    返回结果如下:

    {
        "sex": [
            "该字段是必填项。"
        ],
        "age": [
            "该字段是必填项。"
        ]
    }
    

    测试全局钩子

    输入测试数据

    {
        "name": "kkk",
        "sex": 1,
        "age": 18
    }
    

    返回结果如下:

    {
        "age&sex": [
            "男的必须22周岁以上才能结婚"
        ]
    }
    

    总结

    1. 设置必填与选填序列化字段,设置校验规则
    2. 为需要额外校验的字段提供局部钩子函数,如果该字段不入库,且不参与全局钩子校验,可以将值取出校验
    3. 为有联合关系的字段们提供全局钩子函数,如果某些字段不入库,可以将值取出校验
    4. 重写create方法,完成校验通过的数据入库工作,得到新增的对象
  • 相关阅读:
    C语言学习第八章
    C语言学习第七章
    C语言学习第六章
    C语言学习第五章
    ssh的bug
    Oracel 用户管理
    初识Kettle
    IDEA使用MAVEN时自动创建骨架卡的设置
    2017/6/12 JSON
    DDL,DML,DQL
  • 原文地址:https://www.cnblogs.com/jiakecong/p/14849889.html
Copyright © 2020-2023  润新知