2.DRF 序列化器-Serializer

发布于:2025-08-01 ⋅ 阅读:(20) ⋅ 点赞:(0)


序列化器 --Serializer 使用

作用:

  • 序列化:序列化会把模型对象转换成字典,经过 response 以后变成 json 字符串
  • 反序列化:把客户端发送过来的数据,经过 request 以后变成字典,序列化器可以把字典转成模型
  • 反序列化:完成数据校验功能

一.定义序列化器

Django REST framework 中的 Serializer 使用类来定义,序列化器类必须直接或间接继承自 rest_framework.serializers.Serializer。Serializer有个子类叫:ModelSerializer

创建一个子应用来 sers 来演示序列化器的使用

python manage.py startapp sers

注册子应用,并创建 urls 子路由文件,最后在总路由中注册子路由

settings.py 代码

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    "rest_framework",
    "sers",  # 序列化器
]

sers.urls,代码

from django.urls import path
from . import views
urlpatterns = [
    path('students/', views.StudentView.as_view()),
]

drfdemo.urls 代码

urlpatterns = [
    path("sers/", include("sers.urls")),

创建一个数据库模型类, sers.models 代码

from django.db import models


# Create your models here.
class Student(models.Model):
    """学生信息"""
    name = models.CharField(max_length=255, verbose_name="姓名", help_text="学生姓名")
    sex = models.BooleanField(default=1, verbose_name="性别", help_text="学生性别性别")
    age = models.IntegerField(verbose_name="年龄", help_text="用户年龄")
    classmate = models.CharField(max_length=5, verbose_name="班级编号", help_text="学生班级编号")
    description = models.TextField(max_length=1000, verbose_name="个性签名", help_text="学生个性签名")

    class Meta:
        db_table = "tb_student"
        verbose_name = "学生信息"
        verbose_name_plural = verbose_name

为这个模型类提供一个序列化器,定义如下

sers.serializers.py 代码

class StudentSerializer1(serializers.Serializer):
    """
    模型序列化器
    """
    # 1.转化字段声明
    # 字段 = serializers.字段类型(字段选项)
    id = serializers.IntegerField()
    name = serializers.CharField()
    sex = serializers.BooleanField()
    age = serializers.IntegerField()
    description = serializers.CharField()

    # 2.如果当前序列化器继承的是 ModelSerializer,则需要声明调用的模型信息
    # class Meta:
    #     model = 模型
    #     fields = ["字段1", "字段2", ...]

    # 3.验证代码的对象方法
    # def validate(self, attrs): # validate 是固定的
    #     pass
    #     return attrs

    # def validate_字段名(self, value): # 方法名的格式必须以 validate_字段名 为名称,否则序列化器不能识别
    #     # 验证字段名
    #     return value

    # 4.模型的操作方法
    # def create(self, validated_data): # 完成添加操作,添加数据以后,就自动实现了从字段变成模型对象的过程
    #     pass
    #
    # def update(self, instance, validated_data): # 完成添加操作,添加数据以后,就自动实现了从字段变成模型对象的过程
    #     pass

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

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 最大值

通用参数:

参数名称 说明
read_only 表明该字段仅用于序列化输出,默认False
write_only 表明该字段仅用于反序列化输入,默认False
required 表明该字段在反序列化时必须输入,默认True
default 反序列化时使用的默认值
allow_null 表明该字段是否允许传入None,默认False
validators 该字段使用的验证器,自定义验证函数
error_messages 包含错误编号与错误信息的字典
label 用于HTML展示API页面时,显示的字段名称,
如果没有设置,则会出现英文的字段名或者模型中verbose_name
help_text 用于HTML展示API页面时,显示的字段帮助提示信息
如果没有设置,则会出现英文的字段名或者模型中help_text

二、创建 Serializer 对象

定义好Serializer类后,就可以创建Serializer对象了。

Serializer的构造方法为:

Serializer(instance=None, data=empty, many=True/False, **kwarg)

说明:

1)用于序列化时,将模型类对象传入instance参数

2)用于反序列化时,将要被反序列化的数据传入data参数

3)转换数据可以是一条也可以是多条,如果多条数据,则需要通过many=True来设置。

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

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

通过context参数附加的数据,可以通过Serializer对象的context属性获取。

  1. 使用序列化器的时候一定要注意,序列化器声明了以后,不会自动执行,需要我们在视图中进行调用才可以。
  2. 序列化器无法直接接收数据,需要我们在视图中创建序列化器对象时把使用的数据传递过来。
  3. 序列化器的字段声明类似于我们前面使用过的表单系统。
  4. 开发restful api时,序列化器会帮我们把模型数据转换成字典.
  5. drf提供的视图会帮我们把字典转换成json,或者把客户端发送过来的数据转换字典.

三、序列化器的使用

序列化器的使用分两个阶段:

  1. 在客户端请求时,使用序列化器可以完成对数据的反序列化。
  2. 在服务器响应时,使用序列化器可以完成对数据的序列化。

3.1 序列化

3.1.1 基本使用

  1. 先查询出一个学生

    from students.models import Student
    
    # student = Student.objects.get(id=3)
    student = Student.objects.first()
    
  2. 构造序列化器对象

    from .serializers import StudentSerializer
    # serializer = StudentSerializer(instance=student)
    serializer = StudentSerializer(instance=student)
    
  3. 获取序列化数据

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

    serializer.data
    # {'id': 4, 'name': '小张', 'age': 18, 'sex': True, 'description': '猴赛雷'}
    

    视图代码

    """使用django原生提供的django.views.View"""
    from django.views import View
    from students.models import Student
    from .serializers import StudentSerializer
    from django.http.response import JsonResponse
    class Student1View(View):
        def get1(self,request):
            """
            使用序列化器对数据进行序列化格式转换,提供给客户端
            序列化器的序列化阶段
            """
            """序列化一条数据"""
            # 对数据进行查询
            student = Student.objects.first()
            """实例化序列化器得到序列化器对象"""
            # StudentSerializer(instance=student)  # instance是第一个参数,可以不填
            serializer = StudentSerializer(student)
            # print(f"serializer={serializer}")
            # 获取到格式转换后的数据,data内转换
            # print(f"serializer.data={serializer.data}")
            return JsonResponse(serializer.data)
    
  4. 如果要被序列化的是包含多条数据的查询集QuerySet,可以通过添加many=True参数补充说明

    def get(self,request):
            """使用序列化器对数据进行序列化格式转换,提供给客户端
            序列化器的序列化阶段
            """
            """序列化多条数据"""
            # 1. 对数据进行查询
            student_list = Student.objects.all()
            # 2. 实例化序列化器对象,并传入要转换的数据
            serializer = StudentSerializer(student_list,many=True)
            print(serializer.data)
            """
            [
                OrderedDict([('id', 1), ('name', '赵华'), ('age', 22), ('sex', 1), ('description', '对于勤奋的人来说,成功不是偶然;对于懒惰的人来说,失败却是必然。')]), 
                OrderedDict([('id', 2), ('name', '程星云'), ('age', 20), ('sex', 1), ('description', '人生应该如蜡烛一样,从顶燃到底,一直都是光明的。')]),
                ...
            ]
            OrderedDict 是python内置的高级数据类型,叫有序字典。主要为了解决基本数据类型的dict在存储数据时的随机性问题。
            OrderedDict 的使用和基本类型的dict是一模一样的。可以保证,写入的数据格式顺序在读取时保持一致。
            from collections import OrderedDict
            """
            
            return JsonResponse(serializer.data, safe=False)
    

    注意:序列化器的序列化器阶段,不仅可以转换模型对象的数据格式,只要是python中的对象,属性对应序列化器中的字段,都可以完成转换成字典或者列表。

3.2 反序列化

3.2.1 数据验证

使用序列化器进行反序列化时,需要对数据进行验证后,才能获取验证成功的数据或保存成模型类对象。

在获取反序列化的数据前,必须调用序列化器对象提供的**is_valid()**方法进行调用序列化内部预先写好的验证代码,验证成功返回True,否则返回False。

验证失败,可以通过序列化器对象的errors属性获取错误信息(字典格式),包含了字段和字段的错误提示。如果是非字段错误,可以通过修改REST framework提供的配置选项NON_FIELD_ERRORS_KEY来控制错误字典中的键名。

验证成功,可以通过序列化器对象的validated_data属性获取验证后的数据。

在定义序列化器时,指明每个字段的序列化类型和选项参数,当然这个设置本身就是一种验证行为。

如我们前面定义过的StudentSerializer

sers.serializers,代码:

from rest_framework import serializers
# 声明序列化器,所有的序列化器都要直接或者间接继承于 Serializer
# 其中,ModelSerializer是Serializer的子类,ModelSerializer在Serializer的基础上进行了代码简化
class StudentSerializer(serializers.Serializer):
    """学生信息序列化器"""
    # 1. 声明在请求和响应过程中,需要进行数据转换的字段
    # read_only=True 表示用户提交数据进行反序列化时,序列化器不会启用这个字段,而是在序列化器进行序列化转换数据给客户端时才启用这个字段
    id = serializers.IntegerField(read_only=True)
    # required=True 表示用户移交数据时,这个字段时必填的
    # max_length=20 表示当前字符串字段最大长度为20个字符
    name = serializers.CharField(required=True, max_length=20)
    classmate = serializers.CharField(required=True, max_length=3)
    age = serializers.IntegerField()
    sex = serializers.IntegerField()
    # required=False 表示当前字段,用户提交数据时,可以不提交这个字段的数据
    # default        表示当前字段,用户提交数据时,没有值的情况下,采用这个值作为默认值传递验证结果的返回值中。
    description = serializers.CharField(required=False, default="这家伙很懒,什么也没写!")
    
    # 2. 如果序列化器集成的是ModelSerializer,则需要声明调用的模型信息

    # 3. 验证代码

    # 4. 编写模型的添加和更新代码

通过构造序列化器对象,并将要反序列化的数据传递给data构造参数,进而进行验证

sers.views,代码:

def get(self,request):
        """反序列化一般就是在用户添加数据或更新数据时使用"""
        """添加数据时的反序列化"""
        # 模拟用户提交过来的数据
        params = {
            "name": "王小明",
            "age": 17,
            "sex": 1,
        }

        # 1. 实例化序列化器
        serializer = StudentSerializer(data=params)
        # raise_exception=True 表示在反序列化验证数据失败以后,由drf直接抛出异常,返回错误提示给客户端
        # drf将来提供的视图类会自动把错误信息转换成json数据提供给客户端
        # serializer.is_valid(raise_exception=True)
        ret = serializer.is_valid()
        print(f"验证结果={ret}") # 验证数据通过则True,否则为False
        if ret:
            # 获取验证后的结果,注意此时我们还没有学习到自动保存数据,所以这里仅仅是验证而已,没有到数据库中呢
            print(f"serializer.validated_data={serializer.validated_data}")
        else:
            # 获取验证后的错误
            print(f"serializer.errors={serializer.errors}")
            for item in serializer.errors.items():
                print(f"字段名={item[0]}")
                for error in item[1]:
                    print(error.code)
                    print(error)


        return JsonResponse({"msg":"ok"})

is_valid()方法还可以在验证失败时抛出异常serializers.ValidationError,可以通过传递raise_exception=True参数开启,REST framework接收到此异常,会向前端返回HTTP 400 Bad Request响应。

sers.views,代码:

def get(self,request):
        """反序列化一般就是在用户添加数据或更新数据时使用"""
        """添加数据时的反序列化"""
        # 模拟用户提交过来的数据
        params = {
            "name": "王小明",
            "age": 17,
            "sex": 1,
        }

        # 1. 实例化序列化器
        serializer = StudentSerializer(data=params)
        # raise_exception=True 表示在反序列化验证数据失败以后,由drf直接抛出异常,返回错误提示给客户端
        # drf将来提供的视图类会自动把错误信息转换成json数据提供给客户端
        serializer.is_valid(raise_exception=True)
        return JsonResponse({"msg":"ok"})

is_valid会自动调用序列化器中所有的验证代码,上面is_valid就调用了我们在序列化器字段声明的选项对数据进行验证。如果觉得这些还不够,还可以继续补充其他相加详尽的验证代码让is_valid进行调用,你可以使用以下3种方法编写验证代码:

1)单字段验证方法,validate_字段名

<field_name>字段进行验证,如

from rest_framework import serializers
# 声明序列化器,所有的序列化器都要直接或者间接继承于 Serializer
# 其中,ModelSerializer是Serializer的子类,ModelSerializer在Serializer的基础上进行了代码简化
class StudentSerializer(serializers.Serializer):
    """学生信息序列化器"""
    # 1. 声明在请求和响应过程中,需要进行数据转换的字段
    # read_only=True 表示用户提交数据进行反序列化时,序列化器不会启用这个字段,而是在序列化器进行序列化转换数据给客户端时才启用这个字段
    id = serializers.IntegerField(read_only=True)
    # required=True 表示用户移交数据时,这个字段时必填的
    # max_length=20 表示当前字符串字段最大长度为20个字符
    name = serializers.CharField(required=True, max_length=20)
    classmate = serializers.CharField(required=True, max_length=3, error_messages={"required":"当前字段必须填写!","max_length": "对不起,长度不能超过3个字符"})
    age = serializers.IntegerField()
    sex = serializers.IntegerField()
    # required=False 表示当前字段,用户提交数据时,可以不提交这个字段的数据
    # default        表示当前字段,用户提交数据时,没有值的情况下,采用这个值作为默认值传递验证结果的返回值中。
    description = serializers.CharField(required=False, default="这家伙很懒,什么也没写!")

    # 2. 如果序列化器集成的是ModelSerializer,则需要声明调用的模型信息

    # 3. 验证代码
    # 3.1 对单个字段数据进行验证,
    # 格式:
    #     一个序列化器中可以有0个或多个验证字段方法,方法名唯一。
    #     必须以 "validate_<字段名>"作为验证方法名,否则is_valid找不到的
    #     验证方法执行时,is_valid会把当前客户端提交的字段数据传递到验证方法,我们需要接收该参数进行验证
    #     验证方法必须把当前字段的数据作为方法的返回值,回传给序列化器,否则该数据丢失。
    def validate_name(self, data):
        print(f"验证方法[validate_nmae]={data}")
        if data == "python":
            # 序列化器中要抛出异常,可以通过serializers.ValidationError()把异常抛出给is_valid进行处理
            raise serializers.ValidationError(code="name", detail="当前数据属于敏感字符!")

        # 必须有返回值,而且返回值必须是当前字段的数据值,可以改动数据,但是必须返回
        return data

    # 4. 编写模型的添加和更新代码

测试,视图代码:

def get(self, request):
        """添加数据时的反序列化[指定更多的验证规则]"""
        # 模拟用户提交过来的数据
        params = {
            "name": "python",
            "age": 17,
            "sex": 1,
            "classmate": 501,
        }

        # 1. 实例化序列化器
        serializer = StudentSerializer(data=params)

        # 2. 调用is_valid进行验证数据
        serializer.is_valid(raise_exception=True)
        # 如果上一行没有抛出异常,则表示验证通过,我们就可以下面代码中获取验证后的数据结果
        print(serializer.validated_data)
        return JsonResponse({"msg":"ok"})
2) 多字段验证方法,validate

在序列化器中需要同时对多个字段进行比较验证时,可以定义validate方法来验证,如

from rest_framework import serializers
# 声明序列化器,所有的序列化器都要直接或者间接继承于 Serializer
# 其中,ModelSerializer是Serializer的子类,ModelSerializer在Serializer的基础上进行了代码简化
class StudentSerializer(serializers.Serializer):
    """学生信息序列化器"""
    # 1. 声明在请求和响应过程中,需要进行数据转换的字段
    # read_only=True 表示用户提交数据进行反序列化时,序列化器不会启用这个字段,而是在序列化器进行序列化转换数据给客户端时才启用这个字段
    id = serializers.IntegerField(read_only=True)
    # required=True 表示用户移交数据时,这个字段时必填的
    # max_length=20 表示当前字符串字段最大长度为20个字符
    name = serializers.CharField(required=True, max_length=20)
    classmate = serializers.CharField(required=True, max_length=3, error_messages={"required":"当前字段必须填写!","max_length": "对不起,长度不能超过3个字符"})
    age = serializers.IntegerField()
    sex = serializers.IntegerField()
    # required=False 表示当前字段,用户提交数据时,可以不提交这个字段的数据
    # default        表示当前字段,用户提交数据时,没有值的情况下,采用这个值作为默认值传递验证结果的返回值中。
    description = serializers.CharField(required=False, default="这家伙很懒,什么也没写!")

    # 2. 如果序列化器集成的是ModelSerializer,则需要声明调用的模型信息

    # 3. 验证代码
    # 3.1 对单个字段数据进行验证,
    # 格式:
    #     一个序列化器中可以有0个或多个验证字段方法,方法名唯一。
    #     必须以 "validate_<字段名>"作为验证方法名,否则is_valid找不到的
    #     验证方法执行时,is_valid会把当前客户端提交的字段数据传递到验证方法,我们需要接收该参数进行验证
    #     验证方法必须把当前字段的数据作为方法的返回值,回传给序列化器,否则该数据丢失。
    def validate_name(self, data):
        print(f"验证方法[validate_nmae]={data}")
        if data == "python":
            # 序列化器中要抛出异常,可以通过serializers.ValidationError()把异常抛出给is_valid进行处理
            raise serializers.ValidationError(code="name", detail="当前数据属于敏感字符!")

        # 必须有返回值,而且返回值必须是当前字段的数据值,可以改动数据,但是必须返回
        return data

    # 3.2 对多个字段的数据进行验证
    # 格式:
    #    一个序列化器中只能有0个或1个多字段验证方法,方法名唯一
    #    方法名必须叫 validated(self, data),否则is_valid找不到的
    #    验证方法执行时,is_valid会把当前客户端提交的所有字段数据传递到验证方法,我们需要接收该参数进行验证
    #    验证方法必须把当前字段的数据作为方法的返回值,回传给序列化器,否则该数据丢失。
    def validate(self, attrs):
        # 多字段的验证一般用于,用户注册时密码和确认密码,2个字段的密码必须一样。这种情况
        # 判断年龄在20以下不能添加到5字头的班级
        age = attrs.get("age")
        classmate = attrs.get("classmate")
        if (age < 20) and str(classmate).startswith("5"):
            raise serializers.ValidationError("对不起!年龄在20以下不能添加到5字头的班级")
        return attrs
    # 4. 编写模型的添加和更新代码

测试

    def get(self, request):
        """添加数据时的反序列化[指定更多的验证规则]"""
        # 模拟用户提交过来的数据
        params = {
            "name": "李小明",
            "age": 17,
            "sex": 1,
            "classmate": 501,
        }

        # 1. 实例化序列化器
        serializer = StudentSerializer(data=params)

        # 2. 调用is_valid进行验证数据
        serializer.is_valid(raise_exception=True)
        # 如果上一行没有抛出异常,则表示验证通过,我们就可以下面代码中获取验证后的数据结果
        print(serializer.validated_data)
        return JsonResponse({"msg":"ok"})
3)单字段验证函数,validators

在序列化器的字段选项中添加validators选项参数,也可以补充验证代码。

选项validators的值是一个列表,列表的成员必须一个函数名,这个函数名不能是字符串。

例如:

from rest_framework import serializers
# 声明序列化器,所有的序列化器都要直接或者间接继承于 Serializer
# 其中,ModelSerializer是Serializer的子类,ModelSerializer在Serializer的基础上进行了代码简化

# validators选项的验证函数可以有0到多个,这些验证函数和写在序列化器内部的验证方法使用方法一致,几乎没区别
# 函数名叫什么无所谓!函数必须有一个参数接收is_valid传递进行的字段数据
# validators是通用的选项,任何字段都可以指定自己多个外部验证函数
# 一般就是验证函数比较通用的时候,没必要把验证数据额代码写在每一个序列化器中时就可以使用外部验证函数了。
def check_sex(data):
    # 必须把数据返回,否则数据丢失了
    if data > 2:
        raise serializers.ValidationError(code="sex", detail="对不起,地球上没有这个性别选项!")

    # 必须返回验证后的数据
    return data

class StudentSerializer(serializers.Serializer):
    """学生信息序列化器"""
    # 1. 声明在请求和响应过程中,需要进行数据转换的字段
    # read_only=True 表示用户提交数据进行反序列化时,序列化器不会启用这个字段,而是在序列化器进行序列化转换数据给客户端时才启用这个字段
    id = serializers.IntegerField(read_only=True)
    # required=True 表示用户移交数据时,这个字段时必填的
    # max_length=20 表示当前字符串字段最大长度为20个字符
    name = serializers.CharField(required=True, max_length=20)
    classmate = serializers.CharField(required=True, max_length=3, error_messages={"required":"当前字段必须填写!","max_length": "对不起,长度不能超过3个字符"})
    age = serializers.IntegerField()
    sex = serializers.IntegerField(validators=[check_sex,])
    # required=False 表示当前字段,用户提交数据时,可以不提交这个字段的数据
    # default        表示当前字段,用户提交数据时,没有值的情况下,采用这个值作为默认值传递验证结果的返回值中。
    description = serializers.CharField(required=False, default="这家伙很懒,什么也没写!")

    # 2. 如果序列化器集成的是ModelSerializer,则需要声明调用的模型信息

    # 3. 验证代码
    # 3.1 对单个字段数据进行验证,
    # 格式:
    #     一个序列化器中可以有0个或多个验证字段方法,方法名唯一。
    #     必须以 "validate_<字段名>"作为验证方法名,否则is_valid找不到的
    #     验证方法执行时,is_valid会把当前客户端提交的字段数据传递到验证方法,我们需要接收该参数进行验证
    #     验证方法必须把当前字段的数据作为方法的返回值,回传给序列化器,否则该数据丢失。
    def validate_name(self, data):
        print(f"验证方法[validate_nmae]={data}")
        if data == "python":
            # 序列化器中要抛出异常,可以通过serializers.ValidationError()把异常抛出给is_valid进行处理
            raise serializers.ValidationError(code="name", detail="当前数据属于敏感字符!")

        # 必须有返回值,而且返回值必须是当前字段的数据值,可以改动数据,但是必须返回
        return data

    # 3.2 对多个字段的数据进行验证
    # 格式:
    #    一个序列化器中只能有0个或1个多字段验证方法,方法名唯一
    #    方法名必须叫 validated(self, data),否则is_valid找不到的
    #    验证方法执行时,is_valid会把当前客户端提交的所有字段数据传递到验证方法,我们需要接收该参数进行验证
    #    验证方法必须把当前字段的数据作为方法的返回值,回传给序列化器,否则该数据丢失。
    def validate(self, attrs):
        # 多字段的验证一般用于,用户注册时密码和确认密码,2个字段的密码必须一样。这种情况
        # 判断年龄在20以下不能添加到5字头的班级
        age = attrs.get("age")
        classmate = attrs.get("classmate")
        if (age < 20) and str(classmate).startswith("5"):
            raise serializers.ValidationError("对不起!年龄在20以下不能添加到5字头的班级")
        return attrs
    # 4. 编写模型的添加和更新代码

测试,视图代码:

	def get(self, request):
        """添加数据时的反序列化[指定更多的验证规则]"""
        # 模拟用户提交过来的数据
        params = {
            "name": "李小明",
            "age": 27,
            "sex": 3,
            "classmate": 501,
        }

        # 1. 实例化序列化器
        serializer = StudentSerializer(data=params)

        # 2. 调用is_valid进行验证数据
        serializer.is_valid(raise_exception=True)
        # 如果上一行没有抛出异常,则表示验证通过,我们就可以下面代码中获取验证后的数据结果
        print(serializer.validated_data)
        return JsonResponse({"msg":"ok"})

3.2.2 反序列化-保存数据

前面的验证数据成功后,我们可以使用序列化器来完成数据反序列化的过程.这个过程可以把数据转成模型类对象.

可以通过实现create()和update()两个方法来实现。

from rest_framework import serializers
from students.models import Student

# 声明序列化器,所有的序列化器都要直接或者间接继承于 Serializer
# 其中,ModelSerializer是Serializer的子类,ModelSerializer在Serializer的基础上进行了代码简化

# validators选项的验证函数可以有0到多个,这些验证函数和写在序列化器内部的验证方法使用方法一致,几乎没区别
# 函数名叫什么无所谓!函数必须有一个参数接收is_valid传递进行的字段数据
# validators是通用的选项,任何字段都可以指定自己多个外部验证函数
# 一般就是验证函数比较通用的时候,没必要把验证数据额代码写在每一个序列化器中时就可以使用外部验证函数了。
def check_sex(data):
    # 必须把数据返回,否则数据丢失了
    if data > 2:
        raise serializers.ValidationError(code="sex", detail="对不起,地球上没有这个性别选项!")

    # 必须返回验证后的数据
    return data

class StudentSerializer(serializers.Serializer):
    """学生信息序列化器"""
    # 。。。。
    
    
    
    # 4. 编写模型的添加和更新代码
    # 添加操作,在视图调用序列化器的save时,必须在序列化器中实现create方法,否则报错
    def create(self, validated_data):
        """添加数据,到模型中的实现过程"""

        # student = Student.objects.create(**validated_data)
        # 如果validated_data有多余的字段,不属于模型对象,则自定义字典
        student = Student.objects.create(
            name=validated_data.get("name"),
            age=validated_data.get("age"),
            sex=validated_data.get("sex"),
            classmate=validated_data.get("classmate"),
            description=validated_data.get("description"),
        )
        # 强烈建议返回添加后的模型对象
        return student

    def update(self, instance, validated_data):
        """
        参数1:instance 本次更新数据的模型对象
        参数2:validated_data 本次客户端提交并经过验证后的数据
        """
        instance.name = validated_data.get("name")
        instance.age = validated_data.get("age")
        instance.sex = validated_data.get("sex")
        instance.classmate = validated_data.get("classmate")
        # 针对可选字段,进行条件判断
        if validated_data.get("description"):
            instance.description = validated_data.get("description")

        # 模型调用ojbects提供的save方法保存数据到数据库中。
        instance.save()
        return instance

实现了上述两个方法后,在反序列化数据的时候,就可以通过save()方法返回一个数据对象实例了

视图,代码:

	def get(self, request):
        """更新数据时的反序列化[验证数据后调用模型保存数据]"""
        # 模拟url地址中的pk值[restful中规定,修改,删除,查询1条数据,需要把id写在地址栏中]
        pk = 140
        # 模拟用户提交过来的数据
        params = {
            "name": "李大明",
            "age": 25,
            "sex": 1,
            "classmate": 401,
        }

        # 1. 先从数据库中获取本次要更新数据的模型对象
        student = Student.objects.get(pk=pk)

        # 2. 实例化序列化器
        serializer = StudentSerializer(instance=student, data=params)
        # 3. 调用is_valid进行验证数据
        serializer.is_valid(raise_exception=True)
        # 4. 调用save方法调用模型存储数据
        # 注意,此处的save是序列化器内部的save方法,不是我们之前模型使用的save方法
        serializer.save()
        # 再次序列化器,把更新后的数据返回给客户端。
        print(serializer.data)
        return JsonResponse(serializer.data)

如果实例化序列化器对象时,没有传递instance实例,则序列化器调用save()方法的时候,create()被调用。

相反,实例化序列化器对象时传递了instance实例,则序列化器调用save()方法的时候,update()被调用。

3.3 附加说明

  1. 在对序列化器进行save()保存时,save方法可以追加默认的额外数据到序列化器中的,这些数据可以在create()和update()中的validated_data参数获取到。
 def save(self, **kwargs):
        assert hasattr(self, '_errors'), (
            'You must call `.is_valid()` before calling `.save()`.'
        )

        assert not self.errors, (
            'You cannot call `.save()` on a serializer with invalid data.'
        )

        # Guard against incorrect use of `serializer.save(commit=False)`
        assert 'commit' not in kwargs, (
            "'commit' is not a valid keyword argument to the 'save()' method. "
            "If you need to access data before committing to the database then "
            "inspect 'serializer.validated_data' instead. "
            "You can also pass additional keyword arguments to 'save()' if you "
            "need to set extra attributes on the saved model instance. "
            "For example: 'serializer.save(owner=request.user)'.'"
        )

        assert not hasattr(self, '_data'), (
            "You cannot call `.save()` after accessing `serializer.data`."
            "If you need to access data before committing to the database then "
            "inspect 'serializer.validated_data' instead. "
        )
        # 把save的参数注入到validated_data中,后面在序列化器的create和upfate方法中通过validated_data参数来获取到
        validated_data = {**self.validated_data, **kwargs}

        # self.instance 就是在实例化序列化器时传递进来的模型对象
        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

2)默认序列化器必须传递所有required的字段,否则会抛出验证异常。

针对一些特殊使用场景,例如用户注册时对数据进行必填的验证,如手机号码,用户名。在修改密码时如果使用了同一个序列化器,我们修改密码的时候是不需要用户在此填写其他注册信息的。这种情况我们就要允许用户在这种情况下,只提交部分数据。序列化器针对这种情况,允许我们使用partial参数来允许反序列时,针对用户提交的部分字段进行校验。也就是说,没有提交的字段,在设置了partial=True时,不会去校验。

视图代码:

	def get(self, request):
        """在特殊业务下,可以让客户端绕过部分字段的验证,仅针对客户端提交的数据进行验证"""

        pk = 140
        params = {'age': 17}

        # 1. 先从数据库中获取本次要更新数据的模型对象
        student = Student.objects.get(pk=pk)
        # 2. 实例化序列化器
        # partial=True 设置只验证有值的字段
        serializer = StudentSerializer(instance=student, data=params, partial=True)
        # 3. 调用is_valid进行验证数据
        serializer.is_valid(raise_exception=True)
        # 4. 调用save方法调用模型存储数据
        # 注意,此处的save是序列化器内部的save方法,不是我们之前模型使用的save方法
        serializer.save()
        return JsonResponse(serializer.data)

调整序列化器,代码:

from rest_framework import serializers
from students.models import Student

# 声明序列化器,所有的序列化器都要直接或者间接继承于 Serializer
# 其中,ModelSerializer是Serializer的子类,ModelSerializer在Serializer的基础上进行了代码简化

# validators选项的验证函数可以有0到多个,这些验证函数和写在序列化器内部的验证方法使用方法一致,几乎没区别
# 函数名叫什么无所谓!函数必须有一个参数接收is_valid传递进行的字段数据
# validators是通用的选项,任何字段都可以指定自己多个外部验证函数
# 一般就是验证函数比较通用的时候,没必要把验证数据额代码写在每一个序列化器中时就可以使用外部验证函数了。
def check_sex(data):
    # 必须把数据返回,否则数据丢失了
    if data > 2:
        raise serializers.ValidationError(code="sex", detail="对不起,地球上没有这个性别选项!")

    # 必须返回验证后的数据
    return data

class StudentSerializer(serializers.Serializer):
    """学生信息序列化器"""
    # 1. 声明在请求和响应过程中,需要进行数据转换的字段
    # read_only=True 表示用户提交数据进行反序列化时,序列化器不会启用这个字段,而是在序列化器进行序列化转换数据给客户端时才启用这个字段
    id = serializers.IntegerField(read_only=True)
    # required=True 表示用户移交数据时,这个字段时必填的
    # max_length=20 表示当前字符串字段最大长度为20个字符
    name = serializers.CharField(required=True, max_length=20)
    classmate = serializers.CharField(required=True, max_length=3, error_messages={"required":"当前字段必须填写!","max_length": "对不起,长度不能超过3个字符"})
    age = serializers.IntegerField()
    sex = serializers.IntegerField(validators=[check_sex,])
    # required=False 表示当前字段,用户提交数据时,可以不提交这个字段的数据
    # default        表示当前字段,用户提交数据时,没有值的情况下,采用这个值作为默认值传递验证结果的返回值中。
    description = serializers.CharField(required=False, default="这家伙很懒,什么也没写!")

    # 2. 如果序列化器集成的是ModelSerializer,则需要声明调用的模型信息

    # 3. 验证代码
    # 3.1 对单个字段数据进行验证,
    # 格式:
    #     一个序列化器中可以有0个或多个验证字段方法,方法名唯一。
    #     必须以 "validate_<字段名>"作为验证方法名,否则is_valid找不到的
    #     验证方法执行时,is_valid会把当前客户端提交的字段数据传递到验证方法,我们需要接收该参数进行验证
    #     验证方法必须把当前字段的数据作为方法的返回值,回传给序列化器,否则该数据丢失。
    def validate_name(self, data):
        print(f"验证方法[validate_nmae]={data}")
        if data == "python":
            # 序列化器中要抛出异常,可以通过serializers.ValidationError()把异常抛出给is_valid进行处理
            raise serializers.ValidationError(code="name", detail="当前数据属于敏感字符!")

        # 必须有返回值,而且返回值必须是当前字段的数据值,可以改动数据,但是必须返回
        return data

    # 3.2 对多个字段的数据进行验证
    # 格式:
    #    一个序列化器中只能有0个或1个多字段验证方法,方法名唯一
    #    方法名必须叫 validated(self, data),否则is_valid找不到的
    #    验证方法执行时,is_valid会把当前客户端提交的所有字段数据传递到验证方法,我们需要接收该参数进行验证
    #    验证方法必须把当前字段的数据作为方法的返回值,回传给序列化器,否则该数据丢失。
    def validate(self, attrs):
        # 多字段的验证一般用于,用户注册时密码和确认密码,2个字段的密码必须一样。这种情况
        # 判断年龄在20以下不能添加到5字头的班级
        age = attrs.get("age")
        classmate = attrs.get("classmate")
        if (age < 20) and str(classmate).startswith("5"):
            raise serializers.ValidationError("对不起!年龄在20以下不能添加到5字头的班级")
        return attrs

    # 4. 编写模型的添加和更新代码
    # 添加操作,在视图调用序列化器的save时,必须在序列化器中实现create方法,否则报错
    def create(self, validated_data):
        """添加数据,到模型中的实现过程"""

        # student = Student.objects.create(**validated_data)
        # 如果validated_data有多余的字段,不属于模型对象,则自定义字典
        student = Student.objects.create(
            name=validated_data.get("name"),
            age=validated_data.get("age"),
            sex=validated_data.get("sex"),
            classmate=validated_data.get("classmate"),
            description=validated_data.get("description"),
        )
        # 强烈建议返回添加后的模型对象
        return student

    def update(self, instance, validated_data):
        """
        参数1:instance 本次更新数据的模型对象
        参数2:validated_data 本次客户端提交并经过验证后的数据
        """
        if validated_data.get("name"):
            instance.name = validated_data.get("name")
        if validated_data.get("age"):
            instance.age = validated_data.get("age")
        if validated_data.get("sex"):
            instance.sex = validated_data.get("sex")
        if validated_data.get("classmate"):
            instance.classmate = validated_data.get("classmate")
        # 针对可选字段,进行条件判断
        if validated_data.get("description"):
            instance.description = validated_data.get("description")

        # 模型调用ojbects提供的save方法保存数据到数据库中。
        instance.save()
        return instance

3.3 模型类序列化器

如果我们想要使用序列化器对应的是Django的模型类,DRF为我们提供了ModelSerializer模型类序列化器来帮助我们快速创建一个Serializer类。

ModelSerializer与常规的Serializer相同,但提供了:

  • 基于模型类自动生成一系列字段
  • 基于模型类自动为Serializer生成validators,比如unique_together
  • 包含默认的create()和update()的实现

3.3.1 定义

比如我们创建一个StudentSerializer

"""模型类序列化器"""
from students.models import Student
class StudentModelSerializer(serializers.ModelSerializer):
    # 1. 声明要转换的字段[当部分字段不是来自于模型类时,就要自己手动声明]
    # 2. 模型类信息
    class Meta:
        model = Student
        fields = "__all__"
  • model 指明参照哪个模型类来转换字段
  • fields 指明为模型类的哪些字段生成,exclude互斥

视图代码

from .serializers import StudentModelSerializer
class Student2View(View):
    """模型类序列化器"""
    def get1(self,request):
        """序列化一条数据"""
        student = Student.objects.first()
        serializer = StudentModelSerializer(instance=student)
        print(serializer.data)
        return JsonResponse(serializer.data)
    def get(self,request):
        """序列化多条数据"""
        student_list = Student.objects.all()
        serializer = StudentModelSerializer(instance=student_list, many=True)
        print(serializer.data)
        return JsonResponse(serializer.data, safe=False)

sers.urls,代码:

from django.urls import path
from . import views

urlpatterns =[
    path("s1/", views.Student1View.as_view()),
    path("s2/", views.Student2View.as_view()),
]

3.3.2 指定字段

1)使用fields来明确字段,__all__表名包含所有字段,也可以写明具体哪些字段,如

"""模型类序列化器"""
from students.models import Student
class StudentModelSerializer(serializers.ModelSerializer):
    # 1. 声明要转换的字段[当部分字段不是来自于模型类时,就要自己手动声明]
    user = serializers.CharField(write_only=True,error_messages={"required":"管理员身份必须填写!"})
    # 2. 模型类信息
    class Meta:
        model = Student
        fields = ["id", "name","sex","classmate","user"]  # 指定要经过序列化器调用的字段填写进来
        # fields = "__all__"
        # exclude = ["description"]  # 把不需要序列化器转换的字段进行排除,保留剩余

注意:

  • 使用exclude可以明确排除掉哪些字段

  • 显式指明字段,一般用于在修改数据库本身已有的外键字段或者数据库模型中没有声明的字段中。

  • 指明只读字段

  • 可以通过read_only_fields指明只读字段,即仅用于序列化输出的字段

from students.models import Student
class StudentModelSerializer(serializers.ModelSerializer):
    # 1. 声明要转换的字段[当部分字段不是来自于模型类时,就要自己手动声明]
    user = serializers.CharField(write_only=True,error_messages={"required":"管理员身份必须填写!"})
    # 2. 模型类信息
    class Meta:
        model = Student
        fields = ["id", "name","sex","classmate","user"]  # 指定要经过序列化器调用的字段填写进来
        # fields = "__all__"
        # exclude = ["description"]  # 把不需要序列化器转换的字段进行排除,保留剩余
        read_only_fields = ["id"]    # 指定只读字段列表,写在这里的字段不需要用户上传,服务端直接返回
        # 当然,此处的id即便我们不声明,drf也会把当前设置为字段字段。

3.3 添加额外字段声明

可以使用extra_kwargs参数为ModelSerializer添加或修改原有的选项参数

"""模型类序列化器"""
from students.models import Student
class StudentModelSerializer(serializers.ModelSerializer):
    # 1. 声明要转换的字段[当部分字段不是来自于模型类时,就要自己手动声明]
    user = serializers.CharField(write_only=True,error_messages={"required":"管理员身份必须填写!"})
    # 2. 模型类信息
    class Meta:
        model = Student
        fields = ["id", "name","sex","classmate","user"]  # 指定要经过序列化器调用的字段填写进来
        # fields = "__all__"
        # exclude = ["description"]  # 把不需要序列化器转换的字段进行排除,保留剩余
        read_only_fields = ["id"]    # 指定只读字段列表,写在这里的字段不需要用户上传,服务端直接返回

        # 字段补充声明
        extra_kwargs = {
            "age": {
                "min_value": 0,
                "max_value": 50,
                "error_messages": {
                    "min_value": "年龄不能负数!",
                    "max_value": "年龄不能超过50岁",
                }
            },
            "name": {
                "error_messages":{
                    "max_length": "学生姓名不能超过20个字符!",
                }
            },
            "classmate": {
                "error_messages": {
                    "required": "班级编号必须填写!"
                }
            }
        }

视图,代码:

from .serializers import StudentModelSerializer
class Student2View(View):
    """模型类序列化器"""
    def get1(self,request):
        """序列化一条数据"""
        student = Student.objects.first()
        serializer = StudentModelSerializer(instance=student)
        print(serializer.data)
        return JsonResponse(serializer.data)
    def get2(self,request):
        """序列化多条数据"""
        student_list = Student.objects.all()
        serializer = StudentModelSerializer(instance=student_list, many=True)
        print(serializer.data)
        return JsonResponse(serializer.data, safe=False)
    def get3(self, request):
        """添加数据时的反序列化"""
        # 模拟用户提交过来的数据
        params = {
            "name": "王小明",
            "age": 17,
            "sex": 1,
            "classmate": "401"
        }

        serializer = StudentModelSerializer(data=params)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return JsonResponse(serializer.data, safe=False)
    def get(self, request):
        """更新数据时的反序列化"""
        # 模拟url地址中的pk值[restful中规定,修改,删除,查询1条数据,需要把id写在地址栏中]
        pk = 142
        # 模拟用户提交过来的数据
        params = {
            "name": "李大黑",
            "age": 25,
            "sex": 1,
            "classmate": 401,
            "user": "root",
        }

        # 1. 先从数据库中获取本次要更新数据的模型对象
        student = Student.objects.get(pk=pk)
        serializer = StudentModelSerializer(instance=student, data=params)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return JsonResponse(serializer.data, safe=False)

3.4 序列化器嵌套使用

在序列化器使用过程中,一般一个序列化器对应一个模型数据。往往因为模型之间会存在外键关联,所以一般在输出数据时不仅要获取当前模型的数据,甚至其他模型的数据也需要同时返回,这种情况下,我们可以通过序列化器嵌套调用的方式,帮我们把当前模型数据进行转换以外还额可以同时转换外键对应的模型数据。

为了方便演示,此处我们再次创建一个新的子应用来完成这块内容的学习。

python manage.py startapp school

注册子应用,settings.py,代码:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    'rest_framework',
    'stuapi',   # django原生实现的API接口
    'students', # drf实现的API接口
    'sers',     # 序列化器的学习
    "school",  # 序列化器嵌套
    'req',      # drf提供的请求与响应
    'demo',    # 视图
]

子应用的子路由文件urls.py,代码:

from django.urls import path
from . import views

urlpatterns = [
]

总路由,urls.py,代码:

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('students/', include("students.urls")),
    path('sers/', include("sers.urls")),
    path('school/', include("school.urls")),
]

为了方便演示嵌套这个过程。所以我们在这里简单创建几个关联模型关系。模型models.py,代码

from django.db import models
from django.utils import timezone as datetime 
# Create your models here.
# 学生     sch_student        1                           id=1   name=xiaoming      
# 成绩     sch_achievement    n    n                      id=17  student_id=1   score=97
# 课程     sch_course              1    n
# 老师     sch_teacher                  1

class Student(models.Model):
    name = models.CharField(max_length=50, verbose_name="姓名")
    age  = models.SmallIntegerField(verbose_name="年龄")
    sex  = models.BooleanField(default=False)
    class Meta:
        db_table = "sch_student"

    def __str__(self):
        return self.name

class Course(models.Model):
    name = models.CharField(max_length=50, verbose_name="课程名称")
    teacher = models.ForeignKey("Teacher", on_delete=models.DO_NOTHING, related_name="course",db_constraint=False)
    class Meta:
        db_table = "sch_course"

    def __str__(self):
        return self.name

class Teacher(models.Model):
    name = models.CharField(max_length=50, verbose_name="姓名")
    sex  = models.BooleanField(default=False)
    class Meta:
        db_table = "sch_teacher"

    def __str__(self):
        return self.name

class Achievement(models.Model):
    score = models.DecimalField(default=0, max_digits=4,decimal_places=1, verbose_name="成绩")
    student = models.ForeignKey(Student,on_delete=models.DO_NOTHING, related_name="s_achievment",db_constraint=False)
    course = models.ForeignKey(Course,on_delete=models.DO_NOTHING,related_name="c_achievement",db_constraint=False)
    create_dtime = models.DateTimeField(auto_created=datetime.now)
    class Meta:
        db_table = "sch_achievement"

    def __str__(self):
        return self.score

数据迁移,终端执行:

python manage.py makemigrations
python manage.py migrate

测试数据,代码:

INSERT INTO students.sch_teacher (id, name, sex) VALUES (1, '李老师', 0);
INSERT INTO students.sch_teacher (id, name, sex) VALUES (2, '曹老师', 1);
INSERT INTO students.sch_teacher (id, name, sex) VALUES (3, '许老师', 1);

INSERT INTO students.sch_student (id, name, age, sex) VALUES (1, '小明', 18, 1);
INSERT INTO students.sch_student (id, name, age, sex) VALUES (2, '小黑', 22, 1);
INSERT INTO students.sch_student (id, name, age, sex) VALUES (3, '小白', 18, 1);
INSERT INTO students.sch_student (id, name, age, sex) VALUES (4, '小红', 17, 0);
INSERT INTO students.sch_student (id, name, age, sex) VALUES (5, '小程', 18, 0);
INSERT INTO students.sch_student (id, name, age, sex) VALUES (6, '小辉', 16, 0);
INSERT INTO students.sch_student (id, name, age, sex) VALUES (7, '小明2', 18, 1);
INSERT INTO students.sch_student (id, name, age, sex) VALUES (8, '小黑2', 22, 1);
INSERT INTO students.sch_student (id, name, age, sex) VALUES (9, '小白2', 18, 1);
INSERT INTO students.sch_student (id, name, age, sex) VALUES (10, '小红2', 17, 0);
INSERT INTO students.sch_student (id, name, age, sex) VALUES (11, '小程2', 18, 0);
INSERT INTO students.sch_student (id, name, age, sex) VALUES (12, '小辉2', 16, 0);

INSERT INTO students.sch_course (id, name, teacher_id) VALUES (1, 'python入门', 1);
INSERT INTO students.sch_course (id, name, teacher_id) VALUES (2, 'python进阶', 2);
INSERT INTO students.sch_course (id, name, teacher_id) VALUES (3, 'python高级', 3);
INSERT INTO students.sch_course (id, name, teacher_id) VALUES (4, 'django入门', 2);
INSERT INTO students.sch_course (id, name, teacher_id) VALUES (5, 'django进阶', 3);
INSERT INTO students.sch_course (id, name, teacher_id) VALUES (6, 'django高级', 3);

INSERT INTO students.sch_achievement (create_dtime, id, score, course_id, student_id) VALUES ('2021-06-16 01:18:25.496000', 1, 100.0, 1, 1);
INSERT INTO students.sch_achievement (create_dtime, id, score, course_id, student_id) VALUES ('2021-06-16 01:18:25.496000', 2, 100.0, 2, 2);
INSERT INTO students.sch_achievement (create_dtime, id, score, course_id, student_id) VALUES ('2021-06-16 01:18:25.496000', 3, 100.0, 3, 3);
INSERT INTO students.sch_achievement (create_dtime, id, score, course_id, student_id) VALUES ('2021-06-16 01:18:25.496000', 4, 78.0, 4, 4);
INSERT INTO students.sch_achievement (create_dtime, id, score, course_id, student_id) VALUES ('2021-06-16 01:18:25.496000', 5, 78.0, 5, 5);
INSERT INTO students.sch_achievement (create_dtime, id, score, course_id, student_id) VALUES ('2021-06-16 01:18:25.496000', 6, 78.0, 6, 6);
INSERT INTO students.sch_achievement (create_dtime, id, score, course_id, student_id) VALUES ('2021-06-16 01:18:25.496000', 7, 78.0, 1, 7);
INSERT INTO students.sch_achievement (create_dtime, id, score, course_id, student_id) VALUES ('2021-06-16 01:18:25.496000', 8, 80.0, 2, 8);
INSERT INTO students.sch_achievement (create_dtime, id, score, course_id, student_id) VALUES ('2021-06-16 01:18:25.496000', 9, 80.0, 3, 9);
INSERT INTO students.sch_achievement (create_dtime, id, score, course_id, student_id) VALUES ('2021-06-16 01:18:25.496000', 10, 80.0, 4, 10);
INSERT INTO students.sch_achievement (create_dtime, id, score, course_id, student_id) VALUES ('2021-06-16 01:18:25.496000', 11, 80.0, 5, 11);
INSERT INTO students.sch_achievement (create_dtime, id, score, course_id, student_id) VALUES ('2021-06-16 01:18:25.496000', 12, 60.0, 6, 12);
INSERT INTO students.sch_achievement (create_dtime, id, score, course_id, student_id) VALUES ('2021-06-16 01:18:25.496000', 13, 100.0, 1, 2);
INSERT INTO students.sch_achievement (create_dtime, id, score, course_id, student_id) VALUES ('2021-06-16 01:18:25.496000', 14, 100.0, 2, 3);
INSERT INTO students.sch_achievement (create_dtime, id, score, course_id, student_id) VALUES ('2021-06-16 01:18:25.496000', 15, 100.0, 3, 4);
INSERT INTO students.sch_achievement (create_dtime, id, score, course_id, student_id) VALUES ('2021-06-16 01:18:25.496000', 16, 78.0, 4, 5);
INSERT INTO students.sch_achievement (create_dtime, id, score, course_id, student_id) VALUES ('2021-06-16 01:18:25.496000', 17, 78.0, 5, 6);
INSERT INTO students.sch_achievement (create_dtime, id, score, course_id, student_id) VALUES ('2021-06-16 01:18:25.496000', 18, 78.0, 6, 7);
INSERT INTO students.sch_achievement (create_dtime, id, score, course_id, student_id) VALUES ('2021-06-16 01:18:25.496000', 19, 78.0, 1, 8);
INSERT INTO students.sch_achievement (create_dtime, id, score, course_id, student_id) VALUES ('2021-06-16 01:18:25.496000', 20, 80.0, 2, 9);
INSERT INTO students.sch_achievement (create_dtime, id, score, course_id, student_id) VALUES ('2021-06-16 01:18:25.496000', 21, 80.0, 3, 10);
INSERT INTO students.sch_achievement (create_dtime, id, score, course_id, student_id) VALUES ('2021-06-16 01:18:25.496000', 22, 80.0, 4, 11);
INSERT INTO students.sch_achievement (create_dtime, id, score, course_id, student_id) VALUES ('2021-06-16 01:18:25.496000', 23, 80.0, 5, 12);
INSERT INTO students.sch_achievement (create_dtime, id, score, course_id, student_id) VALUES ('2021-06-16 01:18:25.496000', 24, 60.0, 6, 1);
新建序列化器,重新指定关联属性的值
一对多或者多对多的序列化器嵌套返回多个数据情况

默认情况下,模型经过序列化器的数据转换,对于外键的信息,仅仅把数据库里面的外键ID返回。

序列化器,serializers.py,代码:

from rest_framework import serializers
from .models import Teacher,Course

class CourseModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = Course
        fields = ["id","name"]

class TeacherModelSerializer(serializers.ModelSerializer):
    # 1. 重写模型对应的外键,指定返回的数据是什么
    course = CourseModelSerializer(many=True)
    class Meta:
        model = Teacher
        fields = ["id","name","course"]

视图,views.py,代码:

# Create your views here.
from .models import Teacher
from .serializers import TeacherModelSerializer
from django.views import View
from django.http.response import JsonResponse

class TeacherView(View):
    def get1(self,request):
        """获取一个老师信息"""
        teacher = Teacher.objects.last()
        serializer = TeacherModelSerializer(instance=teacher)
        return JsonResponse(serializer.data, json_dumps_params={"ensure_ascii":False})

    def get(self,request):
        """获取多个老师信息"""
        teacher = Teacher.objects.all()
        serializer = TeacherModelSerializer(instance=teacher,many=True)
        return JsonResponse(serializer.data, safe=False, json_dumps_params={"ensure_ascii":False})

路由,urls.py,代码:

from django.urls import path
from . import views

urlpatterns = [
    path("s1/", views.TeacherView.as_view())
]
多对一或者一对一的序列化器嵌套返回一个数据情况

序列化器,serializers.py,代码:

class Teacher2ModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = Teacher
        fields = ["id","name"]

class Course2ModelSerializer(serializers.ModelSerializer):
    teacher = Teacher2ModelSerializer()
    class Meta:
        model = Course
        fields = ["id","name","teacher"]

视图,views.py,代码:

from .serializers import Course2ModelSerializer
class CourseView(View):
    def get1(self,request):
        """获取一个课程信息"""
        course = Course.objects.first()
        serializer = Course2ModelSerializer(instance=course)
        return JsonResponse(serializer.data, safe=False, json_dumps_params={"ensure_ascii":False})

    def get(self,request):
        """获取多个课程信息"""
        course = Course.objects.all()
        serializer = Course2ModelSerializer(instance=course,many=True)
        return JsonResponse(serializer.data, safe=False, json_dumps_params={"ensure_ascii":False})

路由,urls.py,代码:

from django.urls import path
from . import views

urlpatterns = [
    path("s1/", views.TeacherView.as_view()),
    path("s2/", views.CourseView.as_view())
]
source选项取代主键数值

在多对一或者一对一的序列化器嵌套中,通过source选项,直接通过外键指定返回的某个字段数据。

序列化器,serializers.py,代码:

class Course2ModelSerializer(serializers.ModelSerializer):
    # teacher = Teacher2ModelSerializer()
    teacher = serializers.CharField(source="teacher.name")
    teacher_id = serializers.CharField(source="teacher.id")
    class Meta:
        model = Course
        fields = ["id","name","teacher","teacher_id"]
直接指定关联深度属性

序列化器,代码:

from rest_framework import serializers
from .models import Teacher,Course,Student

class Course2ModelSerializer(serializers.ModelSerializer):
    # teacher = Teacher2ModelSerializer()
    # teacher = serializers.CharField(source="teacher.name")
    class Meta:
        model = Course
        fields = ["id","name","teacher"]
        # 指定关联深度
        # 从老师模型->课程 = 1
        # 从老师模型->课程->成绩 = 2
        # 从老师模型->课程->成绩->学生 = 3
        # ....
        depth = 1

class StudentModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = Student
        fields = ["id","name","s_achievment"]
        depth = 3

视图代码:

from .serializers import Course2ModelSerializer
class CourseView(View):
    def get1(self,request):
        """获取一个课程信息"""
        course = Course.objects.first()
        serializer = Course2ModelSerializer(instance=course)
        return JsonResponse(serializer.data, safe=False, json_dumps_params={"ensure_ascii":False})

    def get(self,request):
        """获取多个课程信息"""
        course = Course.objects.all()
        serializer = Course2ModelSerializer(instance=course,many=True)
        return JsonResponse(serializer.data, safe=False, json_dumps_params={"ensure_ascii":False})

from .serializers import StudentModelSerializer
class StudentView(View):
    def get(self,request):
        """获取一个学生信息"""
        student = Student.objects.first()
        serializer = StudentModelSerializer(instance=student)
        return JsonResponse(serializer.data, safe=False, json_dumps_params={"ensure_ascii":False})

路由代码:

from django.urls import path
from . import views

urlpatterns = [
    path("s1/", views.TeacherView.as_view()),
    path("s2/", views.CourseView.as_view()),
    path("s3/", views.StudentView.as_view()),
]
自定义模型属性方法

模型,models.py,代码:

from django.db import models
from django.utils import timezone as datetime
# Create your models here.
# 学生     sch_student        1
# 成绩     sch_achievement    n    n
# 课程     sch_course              1    n
# 老师     sch_teacher                  1

class Student(models.Model):
    name = models.CharField(max_length=50, verbose_name="姓名")
    age  = models.SmallIntegerField(verbose_name="年龄")
    sex  = models.BooleanField(default=False)
    class Meta:
        db_table = "sch_student"

    def __str__(self):
        return self.name
    
    # 自定义模型方法
    @property
    def achievement(self):
        return self.s_achievment.values()
        # return self.s_achievment.values("course__teacher__name", "course__name", "score")

class Course(models.Model):
    name = models.CharField(max_length=50, verbose_name="课程名称")
    teacher = models.ForeignKey("Teacher", on_delete=models.DO_NOTHING, related_name="course",db_constraint=False)
    class Meta:
        db_table = "sch_course"

    def __str__(self):
        return self.name

class Teacher(models.Model):
    name = models.CharField(max_length=50, verbose_name="姓名")
    sex  = models.BooleanField(default=False)
    class Meta:
        db_table = "sch_teacher"

    def __str__(self):
        return self.name

class Achievement(models.Model):
    score = models.DecimalField(default=0, max_digits=4,decimal_places=1, verbose_name="成绩")
    student = models.ForeignKey(Student,on_delete=models.DO_NOTHING, related_name="s_achievment",db_constraint=False)
    course = models.ForeignKey(Course,on_delete=models.DO_NOTHING,related_name="c_achievement",db_constraint=False)
    create_dtime = models.DateTimeField(auto_created=datetime.now)
    class Meta:
        db_table = "sch_achievement"

    def __str__(self):
        return str( float(self.score) )

序列化器,代码:

class Student2ModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = Student
        fields = ["id","name","achievement"]

视图代码:

from .serializers import StudentModelSerializer,Student2ModelSerializer
class StudentView(View):
    def get1(self,request):
        """获取一个学生信息"""
        student = Student.objects.first()
        serializer = StudentModelSerializer(instance=student)
        return JsonResponse(serializer.data, safe=False, json_dumps_params={"ensure_ascii":False})

    def get(self,request):
        """获取一个学生信息"""
        student = Student.objects.first()
        serializer = Student2ModelSerializer(instance=student)
        return JsonResponse(serializer.data, safe=False, json_dumps_params={"ensure_ascii": False})

路由,代码:

from django.urls import path
from . import views

urlpatterns = [
    path("s1/", views.TeacherView.as_view()),
    path("s2/", views.CourseView.as_view()),
    path("s3/", views.StudentView.as_view()),
]

网站公告

今日签到

点亮在社区的每一天
去签到