django和drf_jackson序列化原理

django和drf_jackson序列化原理前言上一篇文章我们讲述了序列化,这篇就带大家一起来实现以下序列化Serializer我们使用序列化类Serializer,我们来看下源码结构,这里推荐使用pycharm左边导航栏的Structu

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

前言

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

Serializer

我们使用序列化类Serializer,我们来看下源码结构,这里推荐使用pycharm左边导航栏的Structure,可以清晰的看到一个文件的结构,如下图
django和drf_jackson序列化原理
我们会发现Serializer继承自BaseSerializerSerializerMetaclass,但是Serializer类中又没有create方法和update方法,所以我们使用的时候必须自己手动定义这2个方法
 

准备工作

1.新建一个项目drf_demo,在项目中新建一个appdrf_app,在app中新建一个文件urls.py,项目结构如下
django和drf_jackson序列化原理

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方法,完成校验通过的数据入库工作,得到新增的对象
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/166139.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 置顶文章-波波烤鸭博客文章汇总篇【Java核心,经典开源框架应用及源码分析,企业级解决方案等】强烈建议收藏!!![通俗易懂]

    置顶文章-波波烤鸭博客文章汇总篇【Java核心,经典开源框架应用及源码分析,企业级解决方案等】强烈建议收藏!!![通俗易懂]  因为博客中的文章已经越来越来了,为了便于文章检索,特整理本文,欢迎收藏!!!Java核心1.JDK8新特性Lambda表达式讲解接口新特性函数式接口方法引用Stream流Optional工具类介绍新的日期时间工具类介绍注解的增强2.Java核心Java集合核心内容之数组和链表Java集合核心内容之二叉树2-3-4树详解红黑树详解精讲红黑树删除操作剖析反射的本质3.设计模式3.1创建型模式  都是用来帮助我们创建对象的!模式地址单例模式ht

    2025年10月11日
    4
  • MODIS数据说明

    MODIS数据说明MODIS目前主要存在于两颗卫星上:TERRA和AQUA。TERRA卫星每日地方时上午10:30时过境,因此也把它称作地球观测第一颗上午星(EOS-AM1)。AQUA每日地方时下午过境,因此称作地球观测第一颗下午星(EOS-PM1)。两颗星相互配合,每1-2天可重复观测整个地球表面,得到36个波段(表1)的观测得到,这些数据广泛用于全球陆地、海洋和低层大气内的动态变化过程研究。MODIS获

    2022年5月7日
    89
  • 为何禁用MyBatis缓存

    为何禁用MyBatis缓存privatevoidputAncestor(CacheKeyrowKey,ObjectresultObject,StringresultMapId,StringcolumnPrefix){if(!ancestorColumnPrefix.containsKey(resultMapId)){ancestorColumnPrefix.put(resul

    2022年5月17日
    44
  • 安防监控必备的基础知识「建议收藏」

    安防监控必备的基础知识「建议收藏」安防监控必备的基础知识 什么是云镜控制解码器?答:解码器是将前端发出的控制信号转换为电压信号从而控制云台、镜头的的装置。什么是同轴电缆?答:同轴电缆(COARIALCABLE)的得名与它的结构相关。同轴电缆也是监控中最常见的传输介质之一。它用来传递信息的一对导体是按照一层圆筒式的外导体套在内导体(一根细芯)外面,两个导体间用绝缘材料互相隔离的结构制选的,外层导体和中心轴芯线的…

    2022年4月19日
    48
  • noip2012提高组初赛_noip2018提高组初赛解析

    noip2012提高组初赛_noip2018提高组初赛解析Noip2012参赛总结又一年NOIP考完了。刚刚才看了去年自己写的参赛总结,有点后悔考试之前没有看。里面有一句话“NOIP给的数据都是白痴的,一定要多测几组自己的数据,尽管有些数据你相信你的程序一定能过。但往往正是这些数据暴露出了你程序的不足。”对于DAY1的第二题。我想用深搜来做,尽管我知道过不了多少个点,但总比没有好。于是就以很快的速度敲完了深搜,测了两组数据就去做第三题了。离考试

    2022年8月22日
    5
  • 交叉验证方法汇总【附代码】(留一法、K折交叉验证、分层交叉验证、对抗验证、时间序列交叉验证)

    交叉验证方法汇总【附代码】(留一法、K折交叉验证、分层交叉验证、对抗验证、时间序列交叉验证)交叉验证是什么?在模型建立中,通常有两个数据集:训练集(train)和测试集(test)。训练集用来训练模型;测试集是完全不参与训练的数据,仅仅用来观测测试效果的数据。一般情况下,训练的结果对于训练集的拟合程度通常还是挺好的,但是在测试集总的表现却可能不行。比如下面的例子:图一的模型是一条线型方程。可以看到,所有的红点都不在蓝线上,所以导致了错误率很高,这是典型的不拟合的情况图二的蓝线则更加贴近实际的红点,虽然没有完全重合,但是可以看出模型表示的关系是正确的。图三,所有点都在蓝线上,这时候

    2025年7月14日
    3

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号