【Django】DRF自定义序列化器、序列化与反序列化

使用Django前后端分离开发的情况下,我们经常需要把处理后的数据按照一定的格式(比如说json)返回,或者把前端传过来的数据进行处理才能进行数据库的CURD。而DRF(Django REST Framework)就可以帮忙我解决这个问题,即序列化和反序列化

1.安装DRF

使用pip安装

pip install djangorestframework

配置应用

INSTALLED_APPS = [
    ...
    'rest_framework',
]

2.定义Serializer

我们新建一个py文件专门写序列化类继承serializers.Serializer,写的字段和约束要在模型类范围之内

models.py

from django.db import models

class BookInfo(models.Model):
    btitle = models.CharField(max_length=20, verbose_name='名称')
    bpub_date = models.DateField(verbose_name='发布日期', null=True)
    bread = models.IntegerField(default=0, verbose_name='阅读量')
    bcomment = models.IntegerField(default=0, verbose_name='评论量')
    image = models.ImageField(upload_to='booktest', verbose_name='图片', null=True)

serializers.py

from rest_framework import serializers

class BookInfoSerializer(serializers.Serializer):
    """图书数据序列化器"""
    id = serializers.IntegerField(label='ID', read_only=True)
    btitle = serializers.CharField(label='名称', max_length=20)
    bpub_date = serializers.DateField(label='发布日期', required=False)
    bread = serializers.IntegerField(label='阅读量', required=False)
    bcomment = serializers.IntegerField(label='评论量', required=False)
    image = serializers.ImageField(label='图片', required=False)

说明:Serializer的字段类型几乎与Models的一样,记不起来时直接查看serializers类即可
在这里插入图片描述

选项参数 说明
max_length 最大长度
min_lenght 最小长度
allow_blank 是否允许为空
trim_whitespace 是否截断空白字符
max_value 最大值
min_value 最小值
通用参数 说明
read_only 表明该字段仅用于序列化输出,默认False
write_only 表明该字段仅用于反序列化输入,默认False
required 表明该字段在反序列化时必须输入,默认True
default 反序列化时使用的默认值
allow_null 表明该字段是否允许传入None,默认False
validators 该字段使用的验证器
error_messages 包含错误编号与错误信息的字典
label 用于HTML展示API页面时,显示的字段名称
help_text 用于HTML展示API页面时,显示的字段帮助提示信息

3. 创建Serializer对象

Serializer(instance=None, data=empty, **kwarg)

说明:

  • 序列化时,instance传入模型类对象
  • 反序列化时,data传入被反序列化的数据

4.简单序列化

序列化单个对象

from booktest.models import BookInfo  # 导入图书模型类
from booktest.serializers import BookInfoSerializer # 导入序列器类

book = BookInfo.objects.get(id=2) # 获取一个单一模型类对象
serializer = BookInfoSerializer(book) # 序列化单一模型类对象

serializer.data # 查看序列化之后的数据

序列化查询集

books = BookInfo.objects.all()
serializer = BookInfoSerializer(books, many=True) # 要指定many=True
serializer.data  # 查看结果

5.关联嵌套序列化(外键)

如果存在外键,在序列化的时候可能是序列化主表或从表两大方向

models.py

class BookInfo(models.Model):
	...
    def __str__(self):
        return self.btitle

class HeroInfo(models.Model):
	...
    hbook = models.ForeignKey(BookInfo, related_name='heros',on_delete=models.CASCADE, verbose_name='图书')  # 外键
    def __str__(self):
        return self.hname

5.1 序列化主表

serializers.py

class HeroInfoSerializer(serializers.Serializer):
    id = serializers.IntegerField()
    hname = serializers.CharField()
    hgender = serializers.IntegerField()
    hcomment = serializers.CharField()
    is_delete = serializers.BooleanField()

class BookInfoSerializer(serializers.Serializer):
    id = serializers.IntegerField()
    btitle = serializers.CharField()
    bpub_date = serializers.DateField()
    bread = serializers.IntegerField()
    bcomment = serializers.IntegerField()
    is_delete = serializers.BooleanField()
    image = serializers.ImageField()

    序列化主表BookInfo的时候,它的属性heros是关联的从表的多个对象

    关联对象序列化方案一:序列化成关联对象的主键
    heros = serializers.PrimaryKeyRelatedField(
        queryset=HeroInfo.objects.all(), 
        many=True # 表示当前字段heros序列化的是多个英雄对象
    )

    关联对象序列化方案二:序列化成关联对象的__str__方法返回的结果
    heros = serializers.StringRelatedField(many=True)
    
    关联对象序列化方案三:自定义关联对象序列化
    heros = HeroInfoSerializer(many=True)
5.1 序列化从表

serializers.py

class BookInfoSerializer(serializers.Serializer):
    btitle = serializers.CharField()
    bpub_date = serializers.DateField()
    bread = serializers.IntegerField()
    bcomment = serializers.IntegerField()
    is_delete = serializers.BooleanField()
    image = serializers.ImageField()

class HeroInfoSerializer(serializers.Serializer):
    id = serializers.IntegerField()
    hname = serializers.CharField()
    hgender = serializers.IntegerField()
    hcomment = serializers.CharField()
    is_delete = serializers.BooleanField()

    代表当前英雄对象(),关联的书本()单一对象
    hbook = serializers.PrimaryKeyRelatedField(
        queryset=BookInfo.objects.all()
    )
    hbook = serializers.StringRelatedField()
    
    hbook = BookInfoSerializer()

三种形式说明
(1)、PrimaryKeyRelatedField(queryset,many)
(2)、StringRelatedField()
(3)、自定义序列化器

6.反序列化

6.1 操作步骤

(1)获取有效数据

book_info = {
    
    "btitle": "围城", "bpub_date": "1999-9-9"}

(2)实例化序列器对象

bs = BookInfoSerializer(data=book_info, partial=True)

data是要被校验的数据,partial为True是部分校验,有什么就校验什么

(3)启动校验流程

bs.is_valid()

# 通过抛ValidationError异常的方式表示失败
# bs.is_valid(raise_exception=True)

当调用is_valid()方法即开始校验,包括约束条件校验、类型校验、validators校验、validate_<字段名>校验、validate校验,若全部校验通过返回True,否则返回False

(4)获得最终结果

若校验成功,查看有效数据
bs.validated_data

若校验失败,查看失败信息
bs.errors

(3)自定义校验之1:validators

serializers.py

# 针对btitle字段,自定义一个校验函数
def check_btitle(value):
    if "django" not in value:
        raise serializers.ValidationError("这不是一本关于django的书")

class BookInfoSerializer(serializers.Serializer):
	# validators指定多个针对当前字段的校验函数)
    btitle = serializers.CharField(其他校验,validators=[check_btitle] 
    ...

(3)自定义校验之2:validate_<字段名>

serializers.py

class BookInfoSerializer(serializers.Serializer):
	...
    btitle = serializers.CharField(label='名称',max_length=20,)

    # 重写序列化器特定名称的方法,针对特定字段单独自定义校验,函数名称:validate_<被校验字段名>
    def validate_btitle(self, value):
        if "django" not in value:
            raise serializers.ValidationError("这不是一本关于django的书")

        # 切记,一定要返回字段的有效值,如果不返回会造成当前字段数据丢失
        return value

(3)自定义校验之3:validate

serializers.py

class BookInfoSerializer(serializers.Serializer):
	...
    btitle = serializers.CharField(label='名称',max_length=20,)

    def validate(self, attrs):
    	# 提取数据
        btitle = attrs.get('btitle')
        if 'django' not in btitle:
            raise serializers.ValidationError('这不是一本关于django的书')
        # 相同方式对别的字段校验...略
        # 返回最终的有效数据,如果不返回,后果就是丢失最终的有效数据
        return attrs

第3种校验方式最常用,因为它可以对所以字段进行自定义校验

6.2 新建和更新操作

前端传过来的数据要写进数据库,可能是新建一条新的数据,也可能只是更新部分字段,在调用save()方法的时候会执行新建或更新操作

实例化序列号器对象的时候,如果只传入data不传instance即为新建,若两者都传即为更新数据

新建和更新的本质是调用我们在序列化器重写的create()和update()这两个方法

serializers.py

class BookInfoSerializer(serializers.Serializer):
	 btitle = serializers.CharField(label='名称',max_length=20)
	 ...
     def create(self, validated_data):
        instance = BookInfo.objects.create(**validated_data) # create(btitle="django精通"....)
        return instance

    def update(self, instance, validated_data):
        for field,value in validated_data.items():
            # field = "btitle"; value = "django精通新版"
            setattr(instance, field, value)
        instance.save()
        return instance

举例

book_info = {
    
    "btitle": "django精通"....}
book = BookInfo.objects.get(pk=6)
bs = BookInfoSerializer(instance=book, data=book_info)
bs.is_valid()
# 如果校验结果为True,则调用save()方法
bs.save()

7.补充

7.1 PrimaryKeyRelatedField作用于反序列化
  • PrimaryKeyRelatedField作用于序列化的时候,把关联对象序列化成主键
  • PrimaryKeyRelatedField作用与反序列化的时候,把主键反序列化成关联的对象
7.2 全更新与部分更新
  • 全更新就是全部字段,必传必校验(默认)
  • 部分更新就是部分字段,必要字段可传可不传,传就会被校验(如更新部分字段操作)
# 全校验
Serializer(instance=模型类对象,data=参数)

# 部分校验(如更新部分字段操作)
Serializer(instance=模型类对象,data=参数,partial=True)
7.3 save方法传递关键字参数

一般情况下是在实例化序列化器的时候就用data参数传输待校验的数据,但是后续可能需要指定其他字段,这时候可以在调用save()方法的时候传入其他参数
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_39147299/article/details/108691882