[django-rest-framework]-序列化器Serializer

序列化器

我们都知道,在开发API接口时,最核心的事情有两点:

  • 将数据库数据处理成前端所需要的格式,并返回;
  • 将前端发送的数据转换成模型对象,并保存到数据库中。 // 序列化和反序列化就分别做了以上的事情。

pythonh中序列化和反序列化的概念:

  • 序列化:将python对象转换成json,xml等主流字符串格式的过程
    • 序列化器可以直接将querySet或模型实例中的数据进行拆分转换,我们可以直接通过视图的Response返回成json字符串。
  • 反序列化:将如json等字符串转换成python对象的过程
    • 我们可以通过视图的request拿到字典格式的前端数据,序列化器可以对其进行校验,并保存到数据库中。
    • 数据校验
    • 数据保存:新增 更新

总之,序列化器其实就是对数据库数据的读和写进行辅助简便处理的类。 所以序列化器和数据库模型类息息相关

序列化类的定义

已有一个数据库模型类UserInfo如下,我们想为此模型类提供序列化器,以此到达快速序列化和反序列化的目的。

(注意:serializer不是只能为数据库模型类的数据定义,也可以为⾮数据库模型类的数据定义。 serializer是独⽴于数据库之外的存在。)

from django.db import models

# Create your models here.
class UserInfo(models.Model):
    #以下模型字段默认时不能为空 null=False
    username = models.CharField(max_length=32, unique=True)
    password = models.CharField(max_length=64)
    group = models.ForeignKey("UserGroup", models.CASCADE)
    roles = models.ManyToManyField("Role")
    user_type = models.IntegerField(choices=((1, '普通用户'),(2, 'VIP'),(3, 'SVIP')))
    class Meta:
        db_table="tb_user"
        verbose_name ="用户"
        verbose_name_plural = verbose_name

class UserGroup(models.Model):
    title = models.CharField(max_length=32)     
    
class Role(models.Model):
    title = models.CharField(max_length=32)
    
class UserToken(models.Model):
    user = models.OneToOneField(to="UserInfo", on_delete=models.CASCADE)
    token = models.CharField(max_length=64)

首先我们需要定义序列化类

我们一般通过继承Serializer或ModelSerializer定义某个序列化器类

注:基本都通过继承ModelSerializer来自定义序列化类,ModelSerializer是serializer的子类,在Serializer的基础上进⾏了代码简化

Serializer

from rest_framework import serializers 

# 声明序列化器,继承Serializer(所有的序列化器都要直接或者间接继承于 Serializer)
class StudentSerializer(serializers.Serializer): 

  # 我们必须在此定义需要进行序列化或者反序列化的字段  
    username = serializers.CharField()  
    name = serializers.CharField(source=“username”)  #  这个name就是对应的数据库中的username字段
    user_type = serializers.CharField(source="get_user_type_display") 
    gp = serializers.CharField(source="group.title")  # 外键 
    rls = serializers.SerializerMethodField()  # 自定义序列化字段
    def get_rls(self, obj):  # obj就是此模型实例,对应表中的一条记录,
		ret = obj.rls + '加一个后缀把'
       	return ret

字段定义注意事项:

1.如果没有参数source参数,则定义的字段名称必须和数据库中字段名称相同

2.如果不想用数据库中的字段名称,可以加source参数,指明此字段来源于数据库中哪个字段

source="数据库字段名"

username = serializers.CharField(source=“name”)  # 这个usemname就是对应的数据库中的name字段

3.很多字段 数据库是以key值存储的,比如user_type,但要想给用户展示的是value值。可以使用source=“get_数据库字段名_display”

source="get_数据库字段名_display"

user_type = user_type(source="get_user_type_display")

4.取外键关联,group是外键,可以直接使用 source=外键字段名.字段名 的方式 取到所关联表的某个字段

source=外键字段名.字段名

gp = serializers.CharField(source="group.title")

5.serializerMethodField

自定义序列化字段返回值 ,值由固定的命名规范方法 get_字段名 的返回值提供,此方法会把字段变成只读

SerializerMethodField必须在继承ModelSerializer后使用,并且SerializerMethodField不能添加validate_user_email这样的验证方法

在我们开发的时候,我么使用序列化器序列化返回的时候,返回的大多都是数据框中一个表中的字段,但是我们还想返回一些其他表(关联表)中的字段数据或者数据库中没有的字段怎么办,或者说返回别的数据,此时可以使用

这里的obj是Serializer的Model的模型实例,对应表中的一条记录

rls = serializers.SerializerMethodField()  # 自定义序列化字段
def get_rls(self, obj):  # obj就是此模型实例,对应表中的一条记录
    ret = obj.rls + '加一个后缀把'
    return ret
  

6.HiddleField隐藏字段

依靠输入,而需要设置默认的值,不需要用户自己post数据过来,也不会显式返回给用户,最常用的就是user

# 获取当前用户,并且隐藏了该字段,不会序列化返回给前端
user = serializers.HiddenField(
	default=serializers.CurrentUserDefault()
)
原理:源码:提取request中的user,前端返回的token中会带上user信息
class CurrentUserDefault(object):
    def set_context(self, serializer_field):
        self.user = serializer_field.context['request'].user

    def __call__(self):
        return self.user

    def __repr__(self):
        return unicode_to_repr('%s()' % self.__class__.__name__
字段定义

字段名 = serializers.字段类型(参数)

常用字段类型

字段 字段构造方式
BooleanField BooleanField()
NullBooleanField NullBooleanField()
CharField CharField(max_length=None, min_length=None, allow_blank=False, trim_whitespace=True)
EmailField EmailField(max_length=None, min_length=None, allow_blank=False)
RegexField RegexField(regex, max_length=None, min_length=None, allow_blank=False)
SlugField SlugField(maxlength=50, min_length=None, allow_blank=False) 正则字段,验证正则模式 [a-zA-Z0-9-]+
URLField URLField(max_length=200, min_length=None, allow_blank=False)
UUIDField UUIDField(format=‘hex_verbose’) format: 1) 'hex_verbose'"5ce0e9a5-5ffa-654b-cee0-1238041fb31a" 2) 'hex'"5ce0e9a55ffa654bcee01238041fb31a" 3)'int' - 如: "123456789012312313134124512351145145114" 4)'urn' 如: "urn:uuid:5ce0e9a5-5ffa-654b-cee0-1238041fb31a"
IPAddressField IPAddressField(protocol=‘both’, unpack_ipv4=False, **options)
IntegerField IntegerField(max_value=None, min_value=None)
FloatField FloatField(max_value=None, min_value=None)
DecimalField DecimalField(max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None) max_digits: 最多位数 decimal_palces: 小数点位置
DateTimeField DateTimeField(format=api_settings.DATETIME_FORMAT, input_formats=None)
DateField DateField(format=api_settings.DATE_FORMAT, input_formats=None)
TimeField TimeField(format=api_settings.TIME_FORMAT, input_formats=None)
DurationField DurationField()
ChoiceField ChoiceField(choices) choices与Django的用法相同
MultipleChoiceField MultipleChoiceField(choices)
FileField FileField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)
ImageField ImageField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)
ListField ListField(child=, min_length=None, max_length=None)
DictField DictField(child=)
选项参数
参数名称 作用
max_length 最大长度
min_lenght 最小长度
allow_blank 是否允许为空
trim_whitespace 是否截断空白字符
max_value 最小值
min_value 最大值

前四个只支持CharField字段,后两个只支持IntegerField字段

通用参数
参数名称 说明
read_only 表明该字段仅用于序列化输出,默认False
write_only 表明该字段仅用于反序列化输入,不用做序列化的输出,也就是只用于数据校验,默认False
required 表明该字段在反序列化时必须输入,默认True
default 反序列化时使用的默认值
allow_null 表明该字段是否允许传入None,默认False
validators 该字段使用的验证器
error_messages 包含错误编号与错误信息的字典
label 用于HTML展示API页面时,显示的字段名称
help_text 用于HTML展示API页面时,显示的字段帮助提示信息

read_only和write_only的进一步说明:

我们的字段默认都是既序列化又需反序列化的。

read_only=True,序列化时序列化出该字段数据,反序列化校验时不校验这个数据

write_only=True,序列化时不序列化这个字段数据,反序列校验时,需要客户端传递这个数据过来进⾏校验
数据校验

在反序列化之前,我们需要对前端传过来的数据进行校验,因此我们需要事先定义好验证条件。

在定义序列化器时,每个字段的序列化类型和参数(比如max_length),对应模型字段的序列化类型和参数,本身就会产生一种验证行为。

除此以外, 我们还可以进行补充验证

    1. error_messages 在定义字段时,参数error_messages可为某些参数指定错误提示信息

      class UserInfoSerializer(serializers.ModelSerializer):
          title = serializers.CharField(error_messages={
          																 "blank": "请输入验证码",
                                           "required": "请输入验证码",
                                           "max_length": "验证码格式错误",
                                           })
                                           
      

      当然, 也可以在Meta元类中通过extra_kwargs直接为隐式字段传入额外参数error_messages

          class Meta:
              model = User
              fields = ['id', 'username', 'password', 'mobile', 'passwordConfirm', 'smsCode']
              # 给隐式字段 添加/修改属性
              extra_kwargs = {
                  'username': {
                      'min_length': 5,
                      'max_length': 20,
                      'error_messages': {
                          'min_length': '仅允许5-20个字符的用户名',
                          'max_length': '仅允许5-20个字符的用户名',
                      }
                  },
                  'password': {
                      'write_only': True,
                      'min_length': 8,
                      'max_length': 20,
                      'error_messages': {
                          'min_length': '仅允许8-20个字符的密码',
                          'max_length': '仅允许8-20个字符的密码',
                      }
                  }
              }
      
    1. validators validators参数可为字段补充自定义校验函数;列表格式,元素为验证函数

      • 自定义验证函数(需要在序列化器类以外定义,通常会单独创建一个validators.py文件 从其引入函数

        自定义校验函数需要给定⼀个形参,⽤来接收待校验的数据,并且指定条件下要抛出serializers.ValidationError的异常,如果validators字段值的列表中有多个校验函数,校验过程中会全部进⾏校验,并以列表的形式返回⼀组异常校验信息

      def is_contains_keyword(value):
          if '项目' not in value:
              raise serializers.ValidationError('项目名称中必须得包含“项目”关键字')
      def is_not_contain_x(value):
      		if'X'in value.upper():
                  raise serializers.ValidationError("项⽬名字段name不能包含x的⼤⼩写字符")
      
      name = serializers.CharField(max_length=20, validators=[is_contains_keyword, is_not_contain_x])
      
      • 当然,也可直接调用drf自带的validators模块中的校验函数进行校验,就不用自己定义了。比如唯一校验UniqueValidator

        唯一检验UniqueValidator需要指定查询集和错误提示信息,即传queryset和message两个参数

        除了UniqueValidator的唯⼀校验,还有其它的唯⼀校验,根据不同的场景选择使⽤即可

              extra_kwargs = {
                  'username': {
                      'min_length': 5,
                      'max_length': 20,
                      'error_messages': {
                          'min_length': '仅允许5-20个字符的用户名',
                          'max_length': '仅允许5-20个字符的用户名',
                      },
                      'validators': [UniqueValidator(queryset=User.objects.all(), message='此用户名已注册')],
      
                  },
              }
      
    1. validate_字段名 局部钩子:自定义校验方法

      上面第2种采⽤的是在序列化器类外⾯创建的校验器函数,同样的也可以在序列化器类⾥定义校验器⽅法,也要在指定条件下要抛出serializers.ValidationError的异常,不同的有以下⼏点:

      • ⽅法名必须以 validate_字段名 方式命名
      • ⼀定要返回校验之后的值
      • 不需要放在validators的列表中就可以⽣效,在序列化器里定义即可
      • 自定义校验方法也就是局部钩子
    def validate_mobile(self, mobile):
        # 手机号是否注册
        if User.objects.filter(mobile=mobile).count():
            raise serializers.ValidationError("用户已经存在")
    
        # 验证手机号是否合法
        if not re.match(settings.REGEX_MOBILE,mobile):
            raise serializers.ValidationError('手机号码非法')
    
        # 验证验证码发送频率
        one_minute_ago = datetime.now() - timedelta(hours=0,minutes=1,seconds =0)
        if VerifyCode.objects.filter(add_time__gt=one_minute_ago,mobile=mobile).count():
            raise serializers.ValidationError('请超过60s后再次发送')
        return mobile
    
    1. validate 全局钩子:自定义多字段校验器方法, 对多字段进行扩展验证的逻辑
    • 上面的方法我们都是单字段进行校验,如果是想要同时拿到多字段进行校验,就需要用到该方法

    • 在序列化类中,方法名固定为validate,形参为attrs,attrs为待校验字段组成的字典

    • attrs返回一个QueryDict,字段名可以通过字典的方法进行取值,如:attrs[‘name’] 或者 attrs.get(‘name’)

    • 必须返回形参attrs

    • validate也就是数据校验的全局钩子

      def validate(self, attrs):
      
          # 写校验逻辑
          # 判断注册时填写的两次密码是否一致
          if attrs.get('password') != attrs.get('passwordConfirm'):
          		raise serializers.ValidationError('两次密码不一致')
      
          return attrs  # 全局钩⼦validate最后都要return attrs
      

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WSCWOx7X-1659680817888)(https://img.conglinchu.top/i/image-python/212321312.png)]

      如果验证失败,可以通过序列化器对象的errors属性获取错误信息,返回字典,包含了字段和字段的错误。

      如果是非字段错误,比如全局钩子validate没有验证通过,我们可以看到该异常校验信息字段的key为non_field_errors,

      我们针对这个key可以进⾏修改,可以通过修改REST framework全局配置中的NON_FIELD_ERRORS_KEY来控制错误字典中的键名。

      REST_FRAMEWORK = {
          'NON_FIELD_ERRORS_KEY': 'validate_errors'
      }
      

      这个键名就变成了validate_errors

  • 数据校验顺序

    对每个字段类型,参数进行校验

    调用局部钩子validate_字段名 进行校验

    全局钩子validate进行校验

    使用字段选项中的validators执行外部函数的校验

ModelSerializer

我们都希望序列化器类紧密地映射到模型类上。ModelSerializer做到了这一点。所以我们一般都继承ModelSerializer来定义序列化器。

ModelSerializer类继承于Serializer类,在Serializer的基础上进行了简化,它在Serializer的基础上又提供了以下几点:

  • 基于模型类⾃动⽣成⼀系列字段

  • 基于模型类⾃动为Serializer⽣成validators,⽐如unique_together

  • 包含默认的create()和update()的实现

from rest_framework import serializers 
from .models import UserInfo
class StudentSerializer(serializers.ModelSerializer): 
	# 在此你仍然可以显示的声明字段,就像 Serializer 类一样。
  # 你可以声明和数据库字段同名的字段来覆盖默认字段
  
    class Meta:
    	model = UserInfo  # 指明该序列化器处理的数据字段从模型类UserInfo参考生成
      fileds = '__all__' 
  • 在此你仍然可以显示的声明字段,就像 Serializer 类一样。
  • 你可以声明和数据库字段同名的字段,这样会覆盖数据库中默认字段

在自定义序列化器类中定义元类Meta,关于元类Meta:

  • model 指明模型类对象,来指明此序列化类是为哪张表提供序列化服务。

    • model = UserInfo
  • fields 指明参与该序列化器的字段

    • fileds = ‘__all__’ # 指明 数据库表所有字段 + 自定义的所有字段 进行序列化或反序列化

      (具体参与序列化还是反序列化,还得看字段的write_only 和 read_only 属性)

    • fields = ['‘username’, ‘user_type’] # 指明就列表内的这些字段参加序列化或反序列化 我们一般使用这种

  • exclude = [‘id’, ‘password’] # 排除掉具体某些字段

    fields和exclude不能同时存在,也不能同时不存在

  • read_only_fields 指定多个字段为只读

    • 希望将多个字段指定为只读,而不是显式地为每个字段添加read_only=True属性

    • read_only_fields = [“id”]

    • 只读字段只做序列化,不做反序列化

    • 模型中已经设置 editable=False 的字段 和 默认就被设置为只读的 AutoField 字段都不需要添加到 read_only_fields 选项中。

  • extra_kwargs 给隐式字段添加额外参数

    • 使用 extra_kwargs 选项在字段上指定任意附加关键字参数

    • 一般给默认字段添加,这样就不需要在序列化器中显式得再声明一遍该字段。

    • 比如模型类中的password最大长度是150,想修改到20,直接可以通过extra_kwargs为password字段添加参数

            extra_kwargs = {
                'username': {
                    'min_length': 5,
                    'max_length': 20,
                    'error_messages': {
                        'min_length': '仅允许5-20个字符的用户名',
                        'max_length': '仅允许5-20个字符的用户名',
                    }
                },
                'password': {
                    'min_length': 8,
                    'max_length': 20,
                    'error_messages': {
                        'min_length': '仅允许8-20个字符的密码',
                        'max_length': '仅允许8-20个字符的密码',
                    },
                'user_type': {
                		'source': ’get_user_type_display‘
                		}
                }
            }
    
    • 如果字段已在序列化程序类中显式声明,则extra_kwargs中此字段将被忽略。

    • 注意只是为此字段添加参数,不会改变字段的类型,如果想改变字段的类型,请显示的定义字段来达到覆盖

    • 当然,如果你你不嫌麻烦,可以通过显示定义的方式重新覆盖。

  • depth 外键关系序列化

    • 当Meta写入了depth 将自动序列化外键的连表 depth 代表找嵌套关系的第几层。 depth = 1表示找1层关系
    • 注意:当序列化类META定义了depth时,这个序列化类中引用字段(外键)则自动变为只读。
    • 一般数据库表由于性能原因不使用外键,而且定义depth后,外键自动变为只读,我们一般不使用depth
    class BookSerializer(serializers.ModelSerializer):
        class Meta:
            model = Book        
            fields = "__all__"
            depth = 1  # 建议1或2或3
    

序列化器的使用

在开发REST API的视图中,虽然每个视图具体操作的数据不同,但增、删、改、查的实现流程基本套路化,所以这部分代码也是可以复用简化编写的:

增:校验请求数据 -> 执行反序列化过程 -> 保存数据库 -> 将保存的对象序列化并返回

删:判断要删除的数据是否存在 -> 执行数据库删除

改:判断要修改的数据是否存在 -> 校验请求的数据 -> 执行反序列化过程 -> 保存数据库 -> 将保存的对象序列化并返回

查:查询数据库得到数据库模型对象或querySet -> 将数据序列化并返回

Serializer的构造方法为:

ser = StudentSerializer(instance=None, data=empty, **kwarg)

说明:

  • 用于序列化时,必须将模型类对象或querySet查询集 传入instance参数,且不传入data

  • 用于反序列化时,必须将被反序列化的字典传入data参数

  • 除了instance和data参数外,在构造Serializer对象时,还可通过context参数额外添加数据,如:

    serializer = StudentSerializer(account, context={'request': request})
    

    通过context参数附加的数据,可以通过Serializer实例的context属性获取,如在create方法中:self.context[‘request’]。

    使用场景:

  1. 在视图类中,可以通过request得到登陆用户request.user,在序列化类中,要完成数据库数据的校验与入库操作,可能会需要知道当前的登录用户,但序列化类无法访问request对象,在视图类中实例化序列化对象时,可以通过context参数将request对象传递进去

    book_ser = BookSerializer(context = {'request': request}, instance = objs, data = new_request_data, partial = True, many = True)
    

    在create,update,或validate方法中就可以通过self.context[‘request’] 拿到request

     	2. DRF在序列化图片url字段的时候 会检查上下文有没有request,如果有,就给图片加上域名,也就是绝对路径,没有这个上下文,就会是相对路径,想要绝对路径,就要加上context = {'request': request}
    

反序列化

反序列化即将如json等字符串转换成python对象的过程

我们可以通过视图的request拿到字典格式的前端数据,序列化器可以对其进行校验,并保存到数据库中。

所以我们的关注点是:

  • 创建序列化对象

  • 调用数据验证

  • 对数据进行持久化,包括:新增create 或者 更新update

1.创建序列化器对象
  1. 创建序列化器对象务必传入data,data为字典格式,默认此字典必须包含所有需要反序列化的字段,否则会抛出异常
  • 默认所有的required字段都必须参与反序列化,除非此字段read_only为true

    当然如果此字典中还包括了一些无关的字段,序列化器会忽略掉,没有经过后期校验的字段不会出现在validated_data中

    s = CreateUserSerializer(data=all_list)
    
  • 如果data传递的字典中没有囊括所有需要反序列化的字段,需要增加参数 partial=True(表示部分更新)

    如果未传instance,则表示部分添加(前提是,未传字段对应的数据库字段允许未空!!)

    如果传了instance,则表示对此模型实例进行部分更新

    # 当数据库允许为空,但是序列化器要求必须字段填写的时候,可以使用以下方式避开
    serializer = StudentSerializer(data=part_lsit, partial=True)
    
    更新学生的部分字段信息,           
    updated_server = ServerManager.objects.get(id=request.data['id'])
    s = ServerManagerSerializer(instance=updated_server, data=updated_data, partial=True) 
    

    注意:如果我们漏传需要参加反序列化字段,并且添加了partial=True,那么只会校验传⼊给序列化器的这些数据,但我们的局部钩子中,很可能会取validated_data中某些字段,取不到也有可能被我们抛异常。

    所以要么是真正的部分更新成功,要么会报错,不太会出现漏传却部分更新成功的情况,基本都是我们刻意进行的部分更新。

  • 传递data后,可以通过序列化器实例的initial_data属性 获取将要进行验证的数据

2.调用数据验证is_valid()

进行数据持久化之前,必须调用序列化器实例的**is_valid()**方法进行数据校验

s = CreateUserSerializer(data=all_list)
s.is_valid()  # 成功返回True, 失败返回False
  • 在调用valid()方法以后,序列化器实例的 validated_dataerrors,都会变成可获得的

    s.validated_data 验证成功后,可以通过序列化器实例的validated_data属性 获取通过验证的数据

    s.errors 验证失败,可以通过s.errors获取错误信息

  • is_valid(raise_exception=True)

    is_valid方法还可以在验证失败时抛出异常serializers.ValidationError,可以通过raise_exception=True参数开启,

    一旦检出错误,程序就会终止,自动return。所以我们一般不使用这个参数,直接判断is_valid是否为false,拿s.errors返回即可

3.调用save()数据持久化
在数据校验成功后,调用序列化器的**save()**方法进行数据的保存或更新

其实你不调用save也行,直接在视图中写逻辑
  • 调用序列化器的save()方法后,如果未传instance,save方法则会调序列化器的create()方法

  • 调用序列化器的save()方法后,如果传了instance,save方法则会调序列化器的update()方法

    所以save()方法本质是调用序列化器的create或update方法,save()返回值自然也是create()或者update()方法的返回值

    s = CreateUserSerializer(data=created_list)
    if s.is_valid():
    		s.save()
    
  • 在对序列化器进行save()保存时,可以额外传递数据,这些数据会直接放到validated_data中,当然create和update就可以获取到

    s.save(owner=request.user)  # owner=request.user 会新增在validated_data字典中
    
  • 在调用save()方法以后,序列化器实例的 data 会变成可获得的

    s.data即为反序列化传入数据 成攻创建或更新的数据库模型实例 序列化以后的数据

  • 实际上,save方法也可重写,只不过很少需要

    我们可能不需要创建新的实例,而是使用validated_data中的数据发送电子邮件或做其他的事情。

    这些情况下,可以选择直接重写 .save(),因为这样更具有可读性和意义。

    请注意,在上面的情况下,我们现在必须直接访问序列化器 validated_data 属性。

    def save(self):
    		email = self.validated_data['email']
        message = self.validated_data['message']
        return send_email(from=email, message=message)
    
  • create方法和update方法

    • 如果序列化类继承Serializer,必须重写create方法和update方法,才能实现反序列化数据的新增和更新

    • 如果序列化器类继承ModelSerializer,ModelSerializer自带了create方法和update方法

      ModelSerializer中create方法是对此模型类执行create操作,传参为validated_data

      ModelSerializer中update方法是对此模型类执行update操作,传参为instance, validated_data

    • create方法和update方法 必须返回一个实例

    • ModelSerializer默认create和update方法基本相当于:

      def create(self, validated_data):
      		return ExampleModel.objects.create(**validated_data)
      
      def update(self, instance, validated_data):
          for attr, value in validated_data.items():
              setattr(instance, attr, value)  # setattr(x, 'y', v) is equivalent to ``x.y = v''
          instance.save()
          return instance
        
      # 注:ModelSerializer中的update是使用instance.key = value的方式进行修改的。
      # 如果此模型类没有这个字段,就不会实际进行持久化,也不会报错,所以ModelSerializer的update方法实际上可以不用修改。
      
      
    • 但一般我们总是重写,主要有以下情况:

      • 需要删除 validated_data 中不能保存在数据库中的字段

        进行反序列化校验的参数通常都不一定最后要保存到数据库模型中,数据库也可能没有此字段,比如手机验证码,没有必要保存到数据库中,数据库可能也压根没有此字段。且主要因为数据库模型的创建方法ExampleModel.objects.create无法接收不是数据库字段的参数,会报错。所以我们一般要把validated_data中不能保存在数据库中的字段删除。

        **注:**ModelSerializer中的update()方法是使用instance.key = value的方式进行修改的。如果此模型类没有这个字段,就不会实际进行持久化,也不会报错,所以update方法不用担心validated_data有多余字段的问题,这个情况下就不用重写ModelSerializer中的update()方法了。

      • 调用外部函数

        比如用户密码,需要对明文密码进行哈希处理后再存入数据库,就需要重写

        def create(self, validated_data):
          	
            # 删除  validated_data 中不能保存在数据库中的字段
            del validated_data['passwordConfirm']
            del validated_data['smsCode']
            
            # 先调用ModelSerializer的create方法保存数据,但password现在是明文的
            user = super().create(validated_data) 
            #相当于ExampleModel.objects.create(**validated_data)
            
            # 调用set_password方法重新哈希设置密码  set_password方法是AbstractBaseUser类
            user.set_password(validated_data['password'])
            user.save()
            
            return user  
        

序列化

将数据库模型类转换成字典,并通过视图的Response返回json给前端。

所以我们的关注点是:

  • 创建序列化对象,

  • 传入待序列化的模型实例 或 QuerySet查询集

  • 分页

1.创建序列化器对象
  • 务必传入instance,且不传入data,instance为 模型实例 或 querySet查询集

  • 若要序列化的 是查询集querySet 并不是单个对象实例,实例化时,必须传入many=True

    queryset = Book.objects.all()
    s = BookSerializer(instance=queryset, many=True)
    
  • 实际上,通过源码可知,对于单个对象实例,采用的是Serializer类进行处理, 若对象是QuerySet,采用ListSerializer处理

2.data属性获取数据

通过data属性可以获取序列化后的数据

queryset = Book.objects.all()
s = BookSerializer(instance=queryset, many=True)
s.data # 序列化以后的数据
3.返回序列化后的数据

通过视图的Response将序列化以后的数据,即:data属性 返回成json字符串

class BookListView(APIView):
    def get(self, request, *args, **kwargs):
            queryset = Book.objects.all()  
            s = BookSerializer(instance=queryset, many=True)
            return Response(s.data, status=status.HTTP_200_OK)

4.一般都会搭配drf的分页,这个记录在分页器中。

总结

继承ModelSerializer

  • 明确哪些字段需要序列化,哪些字段需要反序列化,哪些字段只需要序列化,哪些字段只需要反序列化

    • fileds = '__all__' 代表参与序列化和反序列化的字段为:所有的数据库字段 + 自定义的新字段

    • fileds = ['id', 'username'] 代表参与序列化和反序列化的字段就是列表中的这些

    • 序列化器会根据字段的write_only和read_only属性再去判断哪些序列化,哪些反序列化

      我们的字段默认都是既序列化又需反序列化的。

      read_only=True,仅对此字段进行序列化,反序列化校验时不校验这个数据

      write_only=True,此字段不参与序列化,反序列时,需要客户端传递这个数据过来进⾏校验

  • 设置校验规则: 参数校验 ,validtors外部校验函数 , 局部钩子valid_字段名 ,全局钩子validate

  • 重写create或undate方法,如果某些字段不入库,可以将其从validated_data删除。

  • 序列化就是从数据库模型中取数据,比较好操作,主要需要搭配分页。

你可能感兴趣的:(django,python,后端)