• 进入"运维那点事"后,希望您第一件事就是阅读“关于”栏目,仔细阅读“关于Ctrl+c问题”,不希望误会!

Django REST framework Serializer

Python框架 彭东稳 6年前 (2018-05-28) 14512次浏览 已收录 0个评论

一、Serializers

Serializers(序列化器)是什么?序列化器允许将诸如查询集(QuerySet)和模型实例之类的复杂数据转换为原生Python数据类型,然后可以将它们轻松地呈现为JSONXML或其他内容类型。序列化器还提供反序列化,在首次验证传入数据之后,可以将解析的数据转换回复杂类型。

REST framework中的序列化类与Django的FormModelForm类非常相似。我们提供了一个Serializer类,它提供了一种强大的通用方法来控制响应的输出,以及一个ModelSerializer类,它为创建处理模型实例和查询集的序列化提供了有效的快捷方式。

总结来说,serializers有以下几个作用:

– 将queryset与model实例等进行序列化,转化成json格式,返回给用户(api接口)。

– 将post与patch/put的上来的数据进行验证。

– 对post与patch/put数据进行处理。

简单来说,针对get来说,serializers的作用体现在第一条,但如果是其他请求,serializers能够发挥2,3条的作用!下面就来把serializers相关内容过一遍。

二、序列化字段

我们知道在django中,form也有许多field,那serializers其实也是drf中发挥着这样的功能。我们先简单了解常用的几个field。

首先,我们创建一个简单的对象用于示例:

声明一个序列化类,使用它来序列化和反序列化与Comment对象相对应的数据。

声明一个序列化类看起来非常类似于声明一个表单:

不同的是,我们在django中,form更强调对提交的表单进行一种验证,而serializer的field不仅在进行数据验证时起着至关重要的作用,在将数据进行序列化后返回也发挥着重要作用!!更多序列化字段参考“序列化-字段

我们可以看出,不同的field可以用不同的关键参数,除此之外,还有一些十分重要有用的参数。待会介绍。

三、核心参数

每个序列化字段类的构造函数都需要一些参数,某些字段类需要附加特定于该字段的参数,但所有构造函数应始终接受以下参数:

read_only

True表示不允许用户自己上传,只能用于API的输出。如果某个字段设置了read_only=True,那么就不需要进行数据验证,只会在返回时,将这个字段序列化后返回。默认为False。

举个简单的例子:在用户进行购物的时候,用户post订单时,肯定会产生一个订单号,而这个订单号应该由后台逻辑完成,而不应该由用户post过来,如果不设置read_only=True,那么验证的时候就会报错。

write_only

将其设置为True以确保在更新或创建实例时可以使用该字段,但在序列化表示时不包括该字段,默认为False。比如用户注册后返回用户名和相关信息,但不需要返回密码信息,就需要将密码字段设置为write_only。

required

如果在反序列化过程中没有该提供字段,通常会出现错误。如果在反序列化过程中不需要此字段,则应该设置为false,默认为True。

将此设置为False还允许在序列化实例时从输出中省略对象属性或字典密钥。如果密钥不存在,它将不会包含在输出表示中。

allow_null

如果把None传递给序列化字段,通常会引发错误。如果 None 应被视为有效值,则将此关键字参数设置为True 。请注意,将此参数设置为 True 将意味着序列化输出的缺省值为 null,但并不意味着输入反序列化的缺省值。默认为False。

allow_blank

如果为True表示允许为空,空跟None不是一个概念哈。默认为False。

error_messages

出错时的信息提示,格式为一个字典,key是错误代码, value是对应的错误信息。

default

如果设置,则会给出默认值,在没有提供输入值时,将使用该默认值。如果未设置,则默认行为是不填充该属性。

label

字段显示设置,如label=’验证码’。可用作HTML表单字段或其他描述性元素中字段的名称。

source

将用于填充字段的属性的名称,可以是一个只接受 self 参数的方法,如URLField(source=’get_absolute_url’),或者使用点符号来遍历属性,如EmailField(source=’user.email’)。在使用点符号时,如果在属性遍历期间任何对象不存在或为空,则可能需要提供缺省值。

validators

应该应用于传入字段输入的验证函数列表,该列表中的函数应该引发验证错误或仅返回。验证器函数通常应该引发serializers.ValidationError ,但 Django 的内置 ValidationError 也支持与 Django 代码库或第三方 Django 包中定义的验证器兼容。

四、序列化和反序列化对象

现在可以使用 CommentSerializer 来序列化评论或评论列表。同样,使用 Serializer 类看起来很像使用 Form类。

此时已经将模型实例转换为 Python 原生数据类型。为了完成序列化过程,将数据渲染为json。

反序列化是相似的。首先我们将一个流解析为 Python 原生数据类型…

然后我们将这些原生数据类型恢复成通过验证的数据字典。

五、保存实例

如果希望能够基于验证的数据返回完整的对象实例,则需要实现 .create() 和 .update() 方法中的一个或两个。例如:

如果对象实例与 Django 模型相对应,还需要确保这些方法将对象保存到数据库。如果 Comment 是一个 Django 模型,这些方法可能如下所示:

现在,当反序列化数据时,我们可以调用 .save() 根据验证的数据返回一个对象实例。

调用.save()将创建一个新实例或更新现有实例,具体取决于在实例化序列化类时是否传递了现有实例:

这一部分功能是专门为这POST和PUT/PATCH请求所设计的,如果只是简单的GET请求,那么在设置了前面的field可能就能够满足这个需求。

将初始对象或查询集传递给序列化类实例时,该对象将作为.instance提供。如果没有传递初始对象,则.instance属性将为None。将数据传递给序列化类实例时,未修改的数据将作为.initial_data提供。如果 data 关键字参数未被传递,那么.initial_data属性将不存在。

默认情况下,序列化程序必须为所有必填字段传递值,否则会引发验证错误。你可以使用partial参数以允许部分更新。

我们在views以及mixins的博客中提及到,post请求对应create方法,而put/patch请求对应update方法,这里提到的create方法与update方法,是指mixins中特定类中的方法。我们看一下源代码,源代码具体分析可以看到另外一篇博客mixins

可以看出,无论是create与update都写了一行:serializer.save( ),那么,这一行,到底做了什么事情,分析一下源码。

显然,serializer.save的操作,它去调用了serializer的create或update方法,不是mixins中的!!!我们看一下流程图(以post为例)

Django REST framework Serializer

那么,系统是怎么知道,我们需要调用serializer的create方法,还是update方法,我们从save( )方法可以看出,判断的依据是:

如果实例不为None,那么就调用update方法,如果实例为None就调用create方法。简单说就是,如果序列化器传入实例,就表示是PUT/PATCH请求,就要调用update方法;如果未传入实例,就表示是POST请求,就需要调用create方法。

那么我们的mixins的create与update也已经在为开发者设置好了,并且允许部分更新:

也就是说,在update通过get_object( )的方法获取到了instance,然后传递给serializer,serializer再根据是否有传递instance来判断来调用哪个方法!

另外,有时你会希望你的视图代码能够在保存实例的时候注入额外的数据。这些附加数据可能包含当前用户,当前时间或其他任何不属于请求数据的信息。

调用.create().update()时,任何其他关键字参数都将包含在validated_data参数中。

六、验证数据

在反序列化数据时,你总是需要在尝试访问验证数据之前调用is_valid()方法,或者保存对象实例。如果发生任何验证错误,那么.errors属性将包含一个代表错误消息的字典。例如:

字典中的每个键都是字段名称,值是与该字段相对应的错误消息(字符串列表)。non_field_errors 键也可能存在,并会列出任何常规验证错误。可以使用 NON_FIELD_ERRORS_KEY (在 settings 文件中设置)来定制 non_field_errors 关键字的名称。

反序列化 item 列表时,错误将作为代表每个反序列化 item 的字典列表返回。

  • 数据验证时抛出异常

.is_valid()方法带有一个可选的raise_exception标志,如果存在验证错误,将导致它引发serializers.ValidationError异常。

这些异常由REST framework提供的默认异常处理程序自动处理,并且默认情况下将返回HTTP 400 Bad Request

  • 字段级验证

你可以通过向Serializer子类添加.validate_<field_name>方法来指定自定义字段级验证。这些与 Django 表单上的.clean_<field_name>方法类似。这些方法只有一个参数,就是需要验证的字段值。

您的validate_<field_name>方法应返回验证值或引发serializers.ValidationError。例如:

当然,这里面还可以加入很多逻辑,比如判断title是否在数据库中已经存在等。

注意:如果你的序列化程序中声明的 <field_name> 参数为 required = False ,那么如果未包含该字段,则不会执行此验证步骤。

  • 对象级验证

如果要对多个字段进行其他的验证,请将一个名为.validate()的方法添加到您的Serializer子类中。这个方法只有一个参数,它是一个字段值(fieldvalue)的字典。如果有必要,它应该引发一个ValidationError,或者只是返回验证的值。例如:

  • 验证器

序列化器上的各个字段可以包含验证器,方法是在字段实例上声明它们,例如:

当然,drf提供的validators还有很好的功能:UniqueValidator,UniqueTogetherValidator等。UniqueValidator指定某一个对象是唯一的,如,用户名只能存在唯一:

UniqueTogetherValidator表示联合唯一,如用户收藏某个课程,这个时候就不能单独作用于某个字段,我们在Meta中设置。

七、ModelSerializer

讲了很多Serializer的,在这个时候,我还是强烈建议使用ModelSerializer,因为在大多数情况下,我们都是基于model字段去开发。ModelSerializer类提供了一个快捷方式,可让你自动创建一个Serializer类,其中的字段与模型类字段对应。

ModelSerializer类与常规Serializer类相同,不同之处在于:

  • 它会根据模型自动生成一组字段。
  • 它会自动为序列化类生成验证器,例如unique_together验证器。
  • 它包含.create()和.update()的简单默认实现。它能够满足将post或patch上来的数据进行进行直接地创建与更新,除非有额外需求,那么就可以重载create与update方法。

声明ModelSerializer如下,在Meta中设置fields字段,系统会自动进行映射成序列化字段,省去每个字段再写一个field。

你还可以将fields属性设置为特殊值'__all__',以指示应该使用模型中的所有字段。你也可以将exclude属性设置为从序列化程序中排除的字段列表。这三种方式,存在一个即可,且必须有一种方式。

任何关系(如模型上的外键)都将映射到PrimaryKeyRelatedField 。除非在序列化关系文档中指定,否则默认不包括反向关系。

序列化类能够生成一个表示字符串,可以让你充分检查其字段的状态。在使用 ModelSerializer 进行工作时,这是特别有用的,你需要确定它为你自动创建了哪些字段和验证器。为此,使用python manage.py shell进入Django shell,然后导入序列化类,实例化它并打印对象表示形式…

你可以将额外的字段添加到ModelSerializer,或者通过在类上声明字段来覆盖默认字段,就像你对Serializer类所做的那样。

额外的字段可以对应于模型上的任何属性或可调用的字段。

你可能希望将多个字段指定为只读。不要显式给每个字段添加 read_only = True属性,你可以使用快捷方式 Meta 选项 read_only_fields。该选项应该是字段名称的列表或元组,声明如下:

含有 editable = False的模型字段,AutoField 字段默认设置为只读,并且不需要添加到 read_only_fields选项。

  • ModelSerializer需要解决的两个问题

1. 某个字段不属于指定model,它是write_only,需要用户传进来,但我们不能对它进行save( ),因为ModelSerializer是基于Model,这个字段在Model中没有对应,这个时候,我们需要重载validate!如在用户注册时,我们需要填写验证码,这个验证码只需要验证,不需要保存到用户这个Model中:

2. 某个字段不属于指定model,它是read_only,只需要将它序列化传递给用户,但是在这个model中,没有这个字段!我们需要用到SerializerMethodField。假设需要返回用户加入这个网站多久了,不可能维持这样加入的天数这样一个数据,一般会记录用户加入的时间点,然后当用户获取这个数据,我们再计算返回给它。

当然,这个的SerializerMethodField用法还相对简单一点,后面还会有比较复杂的情况。

  • 关于外键的Serializers

其实,外键的field也比较简单,如果我们直接使用serializers.Serializer,那么直接用PrimaryKeyRelatedField就解决了。假设现在有虚拟机信息类别VirtualHost。

ModelSerializer就更简单了,直接通过映射就好了,不过这样用户获得的只是一个外键类别的id,并不能获取到详细的信息(使用PrimaryKeyRelatedField也同样只是获得外键类别的id)。如下,owner字段属于user表的外键。

如果想要获取到具体信息,那就需要嵌套serializer。使用depth选项轻松生成嵌套表示(自关联)。

depth选项应设置为一个整数值,该值指示在还原为平面表示之前应该遍历的关联的深度。

更多情况下,你可能只需要一个外键类别id对应的对象的某个属性值,比如这里只需要在owner处显示其username,可以这么做。

那么这个owner对象从哪里来呢?在DRF中,我们会需要定义权限类,在权限类中返回,类似下面这样:

或者你需要关于用户的更多信息,可以再定义一个关于用户的Serializer,然后在当前序列化类嵌套用户Serializer即可。

上面两种方式,外键都是正向取得,下面介绍怎么反向去取。如,我们需要获取这个用户有哪些主机。首先,在虚拟机VirtualHost的model中,需要在外键中设置related_name。

通过related_name就可以反向取主机信息,一对多关系,一个用户下有多个主机,一定要设定many=True。

外键就基本上就这样了!但是这里还有一个问题,从上面的结果可以看出,我们新增加的virtualhost列默认通过外键获取到用户id,如果想显示用户全部信息就需要使用到SerializerMethodField(method_name=None) ,这是一个只读字段。它通过调用它所连接的序列化类的方法来获得它的值。它可用于将任何类型的数据添加到对象的序列化表示中。

想要什么字段信息,就在VirtualDetailSerializer序列化类中定义就好,呈现结果如下:

<参考>

https://juejin.im/post/5a68934551882573443cddf8

http://drf.jiuyou.info/#/api-guide/fields?id=jsonfield


如果您觉得本站对你有帮助,那么可以支付宝扫码捐助以帮助本站更好地发展,在此谢过。
喜欢 (0)
[资助本站您就扫码 谢谢]
分享 (0)

您必须 登录 才能发表评论!