【Python Web 框架】Django017---drf ---Serializer、序列化类的常用字段、SerializerMethodField、ModelSerializer、局部全局钩子

本文深入探讨Django REST framework中的序列化器概念,包括序列化类的使用、字段参数详解、ModelSerializer的优势及应用场景,并介绍了如何定制字段格式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

昨日内容回顾

函数和方法
在这里插入图片描述

什么是函数,什么是方法?

方法:定义在类当中,有特定含义的,可以自动传值的叫方法
函数:就是普通函数,有几个参数,就要传几个参数
方法得看谁来调,如果是对象的方法,对象来调,就是方法。而类来调,就变成了普通函数

方法有两种:
绑定给类的方法:就是加一个@classmethod装饰器
绑定给对象的方法:就是写在类中,不加任何装饰器的函数,就是绑定给对象的方法

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

CBV源码分析

在这里插入图片描述

上述,路由中其实就是:
IndexView.as_view()执行完后,返回View类中的as_view()方法,它执行完会返回一个内层函数,叫view,内层函数view中执行了self.dispatch();是View类中的dispatch()
dispatch()中的代码执行流程:
    根据用户不同的的请求方式去视图中找到相应的方法,然后执行

drf的APIView的执行流程

我们用了drf之后,都是继承APIView,以及他的子类
匹配到路由之后,会去找as_view,去APIView里面找as_view,返现as_view这个方法,基本没做什么事,就是一个csrf。然后就把父类的as_view拿出来了
super().as_view(**initkwargs),所以,路由里面放的view,就是之前的view,只不过处理了一下csrf。又是执行view()。由于view的内部,执行了
self.dispatch(),因为继承的是APIView的dispatch(),就不会找到View的dispatch()。

APIView的dispatch(),就是3句重要的话:

    -request = self.initialize_request(request, *args, **kwargs)
        包装了新的request
    -self.initial(request, *args, **kwargs)
        执行了3大认证
    -处理了异常(包含三大认证和视图类中的方法)
        处理了异常

APIView中的as_view代码

    view = super().as_view(**initkwargs)
    return csrf_exempt(view)
    -view内部本质执行了self.dispatch()
    -执行了APIView的dispatch
    -request = self.initialize_request(request, *args, **kwargs)
    -self.initial(request, *args, **kwargs)
    -处理了异常(包含三大认证和视图类中的方法)

drf的request对象

request类里面多了一个data属性,其实不是属性,是一个方法,被@property装饰了;前提是没有其他参数(只有self)

在这里插入图片描述

只要继承了APIView,后续的视图类(views中的类)中使用的request对象,都是drf的Request类的对象
-但是用起来,跟之前django的一样
-多了个request.data,post和put提交的数据,都会在里面,字典形式
	-urlencoded编码---》request.data是QueryDict的对象

在这里插入图片描述

    -formdata编码---》request.data是QueryDict的对象
    -json编码---》request.data是dict

在这里插入图片描述

-还多了一个:request.query_params---》它就是原来的request.GET
	-self._request.GET

在这里插入图片描述

	啥也没做?其实drf更规范,人家把名字直接叫成查询参数了

request.data像字典一样用,但是它是谁的对象?取决于前端编码格式
前面两种,是dj处理的,drf直接就把request.POST给了出来
但是json编码,dj没有处理。drf处理了一下,直接转成了字典

今日内容

序列化类值Serializer

下面需要写接口了,前后端需要通过json来交互

5种情况:查所有、查一个、新增一个、修改一个、删除一个;后面写的就这几个curd
    登录:查一条
    注册:增一条

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

序列化类的常用字段

在这里插入图片描述在这里插入图片描述

在这里插入图片描述

现在知道了CharField()、IntegerField();models里面有的,这里都有。重点就是models里面的重点

在这里插入图片描述

ListField
DictField
上面两个之前在models里面没有
重点就是上面几个,其他的了解。想要可以上网查

常用字段参数

字段里面会带一些参数

通过的参数(字段都可以加)
read_only只读	表明该字段仅用于序列化(把py变成json格式的字符串)输出,默认False(重点)
write_only	表明该字段仅用于反序列化输入,默认False(重点)
required	表明该字段在反序列化时必须输入,默认True(了解)
default	    反序列化时使用的默认值(了解)(压根用不到、数据库里面有默认值了,不需要这一层)
allow_null	表明该字段是否允许传入None,默认False(了解)
validators	该字段使用的验证器(写函数的列表,使用这些函数校验该字段)(了解)
error_messages	包含错误编号与错误信息的字典

其他的
CharField专有的
    max_length	最大长度
    min_lenght	最小长度
    allow_blank	是否允许为空
    trim_whitespace	是否截断空白字符

IntegerField专有的
    max_value	最小值
    min_value	最大值
    比如有一本图书最大10000元,最小1元;那么在0-10000之间的数据能存进去,其他的存不进去

下面把5个接口写完

尽量不要把带id的和不带id的放在一个方法里面(虽然可以判断区分,但是不建议)

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

下面在写带id的这种:    尽量不要把带id的和不带id的放在一个类里面(虽然可以判断区分,但是不建议)

在这里插入图片描述
在这里插入图片描述
5个接口写完了,

验证一下序列化器里面的name = serializers.CharField(min_length=3, max_length=8)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

json里面全是双引号

在这里插入图片描述

自我感觉加many=true,传的是列表,不是一个字典

在这里插入图片描述

现在全是英文,需要做国际化

在这里插入图片描述
在这里插入图片描述

改了国际化之后,就能变成中文了

在这里插入图片描述

可以这样实现

在这里插入图片描述

再讲重点read_only、write_only

read_only=True :只用来做序列化(把py对象变成json字符串),往外走有这个字典
下满来验证(验证的过程中,也有很多知识点)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

现在再来增加

在这里插入图片描述

新增成功了,mysql的话肯定新增不了
因为我们用的是sqlite数据库

在这里插入图片描述

在models里面,没有写null = True,填空不行。sqlite是文件数据库,没有那么高的限制,所有就加进来了
因为写了read_only=True,这个字段是没有的,我们可以这样看

在这里插入图片描述

没有name,所以就带着这个数据{'price': 5, 'publish': '北京出版社'},往里面存,sqlite数据库又没有报错,所以能存进去

再看一下write_only

在这里插入图片描述

因为name1在models里面没有

在这里插入图片描述

这时需要指定一个models里面的字段,通过source映射到models里面的字段

在这里插入图片描述
在这里插入图片描述

上面的name1只用来做序列化,在写一个name用来做反序列化

在这里插入图片描述
在这里插入图片描述

自己验证一下下:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

这种写法,就是用用一个类来完成,序列化(往外)和反序列化(写入)
但是说实话,也可以序列化写一个,反序列化写一个
可以写到一起,可以分开

重点:

    -如果想序列化和反序列化都用一个序列化类,可以使用如下俩字段控制
    -read_only	表明该字段仅用于序列化输出,默认False(重点)
    -write_only	表明该字段仅用于反序列化输入,默认False(重点)
    -如果写起来比较麻烦,可以使用两个序列化类,一个序列化,一个反序列化
    source的用法
        name1 = serializers.CharField(source='name')--->意思是name1映射成models中的name

重写update和create

如果继承Serializer类,要修改和保存,一定要重写update和create
BookSerializer---》Serializer---》BaseSerializer--save的核心代码

在这里插入图片描述

validated_data = {**self.validated_data, **kwargs}
if self.instance is not None:
    self.instance = self.update(self.instance, validated_data)
    assert self.instance is not None, (
        '`update()` did not return an object instance.'
    )
else:
    self.instance = self.create(validated_data)
    assert self.instance is not None, (
        '`create()` did not return an object instance.'
    )
return self.instance

在这里插入图片描述

所以,点击调用ser.save()的时候就会调用self.create或self.update方法。这就是为啥一一定要重写重写update和create

写一下update

在这里插入图片描述

我如果不重写update和create?是不是就会报错?

在这里插入图片描述

我们往继承的类里面找,发现有update和create,但是啥事没有做,就报了一个异常
所以:如果继承Serializer类,要修改和保存,一定要重写update和create
后面我们了解了ModelSerializer就不需要写了


上面抛异常,就是一个接口的概念,接口规范了子类的行为,不写就报错。python里面脚鸭子类型
(什么看起来像鸭子,就是鸭子,这些尴尬的话,就不要讲了)

讲鸭子类型的时候:python推崇不需要有一个父类,或者接口约束子类,只要子类里有这些、那些方法,就认为是同一类,这叫鸭子类型
但是我们在实际编码的时候,还是需要父类来约束子类。要么是abc这个模块来约束,要么在子类里面抛异常的方式来约束

指定序列化时某一个字段的格式

先改动一下表

在这里插入图片描述

把库删掉重新迁移一下

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在django2.0以后,都要加on_delete。现在级联删除的话,把出版社删掉,书全部被删掉,非常不合理
只有user和userdetail的时候,把用户删掉,用户详情全部删除合理
所以不仅仅有级联删除,还有do_nothing等等

在这里插入图片描述
在这里插入图片描述

现在查询所有,publish成了一个对象,相对于print(book.publish),这样肯定不合适

SerializerMethodField的使用

我们现在想让他显示publish的名字

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

现在显示的是出版社的名字,要显示出版社的详情来

在这里插入图片描述
在这里插入图片描述

只用来做序列化

在这里插入图片描述

现在想显示出版社的详情
方式一:用的是SerializerMethodField
下面是方式二:
方式二(在模型类中写,方法返回什么,这个字段就是什么)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

方式三(了解一下):

在这里插入图片描述

用source直接映射成这个字段

上面3种都是用来做序列化,第二种用的比较多,在表模型中写

SerializerMethodField的作用就是规定,前端调用的时候,字段显示成啥样

序列化类之ModelSerializer

上面说了Serializer这个类,不好的地方就是需要重写字段
上面的序列化类BookSeriaizer和表模型Book没有必然的关系;其他的模型里面有name。price,就也可以映射。就是说和表模型没有必然联系
ModelSerializer这个是跟表模型是有关系的,后面写项目,基本都是跟表模型有关系的,用的最多

在这里插入图片描述

现在要写publish的5个接口

在这里插入图片描述

直接把Book的复制过来,改成publish就行,配一下路由

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

现在需要在出版社后面都加上_vip

在这里插入图片描述

上面是方式一,在来一个方式二,在models里面写

在这里插入图片描述

可以这样写

在这里插入图片描述

fields = ["name", "addr", "name_detail"]里面的name_detail对着这个def name_detail(self)这个方法,就可以把显示出来
上面是方式二:在表模型中定义一个name_detail,然后再fields里面写上,就可以了
fields里面不仅可以写字段,也可以写方法,方法的返回值,会映射到这个上面来


现在显示的有name、addr、name_detail、3个字段;name字段可以用来,往里写(反序列化)。往外读可以用name_detail(序列化)怎么实现???

在这里插入图片描述
在这里插入图片描述

在fields = ["name", "addr", "name_detail"]里面name_detail是一个方法,默认就不可以往里面传值,所以
name_detail = serializers.CharField(read_only=True) 可以不写

细细捋一下:
现在

在这里插入图片描述
在这里插入图片描述

那我现在添加的时候,只需要添加name和addr就可以了
由于name_detail压根不是字段,是一个方法name_detail = serializers.CharField(read_only=True) 可以不写

在这里插入图片描述

下面再来一个小需求,现在要在addr上面限制一下:最大长度是8,、最小长度是3
    可以重写add,然后写max_length和mix_length
    现在用另一种方式

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

    所以,给字段加一些属性的时候,可以通过extra_kwargs传
    extra_kwargs的作用就是给字段类(比如:name是CharField)传递属性

讲一下depth
在这里插入图片描述
在这里插入图片描述

depth = 1 意思是多序列化一层,如果publish里面还有,就是depth = 2
数字是层数,官方建议最多不超过10层
但是不建议用depth,因为用了之后,里面所有的都会返回,如果想返回某几个,这个不好控制

现在自己实现一下publish不显示id,显示name和addr
两种方式:1.在序列化类中写;2.在表模型中写

再来一个小需求,新增出版社的时候,addr不可以以sb开头

讲一个局部和全局钩子

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

为啥抛的异常没有报错?因为在APIView中捕获了全局异常,帮我们处理了
所以可以尽管抛异常,能捕获到
上面就是局部钩子,可以给某个字段在加校验

还有一个全局钩子
比如多个字段联合校验:比如:出版社名字不能等于出版社的地址(密码等于确认密码等)
写局部钩子限制不住,只能拿到一个字段,同时拿不到
全局钩子也是固定写法

在这里插入图片描述

再介绍一下validators 该字段使用的验证器(写函数的列表,使用这些函数校验该字段)(了解)
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

初遇我ㄖ寸の热情呢?

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值