一、序列化器的简单使用+关联表序列化

啥是序列化器?

数据库的数据 ---------> 前端用的数据 (序列化)

  • 在序列化输出、 如【列出】、【获取详情】,可控制输出哪些字段
  • 和反序列化输入时,如【新增】、【修改】,设置一层校验规则,校验前端传递的参数是否符合要求

序列化器练习的准备

添加注册应用、创建模型

# 创建(练习应用)
startapp project_interface
注册
INSTALLED_APPS = [
    "project_interface",
]

创建model
Projects与Interfaces表,一对多关系

from django.db import models

# Create your models here.

class Projects_test(models.Model):
    name = models.CharField(max_length=20, verbose_name='项目名称', help_text='项目名称',
                            unique=True)
    leader = models.CharField(max_length=10, verbose_name='项目负责人', help_text='项目负责人')
    is_execute = models.BooleanField(verbose_name='是否启动项目', help_text='是否启动项目',
                                     default=True)
    create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间', help_text='创建时间')
    desc = models.TextField(verbose_name='项目描述信息', help_text='项目描述信息',
                            null=True, blank=True, default='')

    class Meta:
        # i.db_table指定创建的数据表名称
        db_table = 'test_projects'
        # 为当前数据表设置中文描述信息
        verbose_name = '项目表'
        verbose_name_plural = '项目表'
        ordering = ['id']

    def __str__(self):
        return f"Projects({self.name})"

class Interfaces_test(models.Model):
    create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间', help_text='创建时间')
    name = models.CharField(verbose_name='接口名称',help_text='接口名称',max_length=20,unique=True)
    tester = models.CharField(verbose_name='测试人员', help_text='测试人员', max_length=10)
    projects = models.ForeignKey(Projects_test, on_delete=models.CASCADE,
                                 verbose_name='所属项目', help_text='所属项目')

    class Meta:
        # db_table指定创建的数据表名称
        db_table = 'test_interfaces'
        # 为当前数据表设置中文描述信息
        verbose_name = '接口表'
        verbose_name_plural = '接口表'
        ordering = ['id']

    def __str__(self):
        return f"Interfaces({self.name})"
生成表:
makemigrations    和 migrate   # 如果是第一次,就先migrate一次整体,生成用户表等
makemigrations project_interface
migrate project_interface				创建表

数据库表的解析

项目表 举例如下 :
这些限制,是数据库限制的,那使用了序列化器后,比如在反序列化输入时,就要提前校验。

  • name 是唯一的
  • desc可以为null可以为空str
  • leader的长度不能超过10

准备数据

py文件测试django数据的方式,会直接往数据库里添加数据
(以下方式,和django shell 中写的效果是一样的), 这种的话方便复制
项目下新增一个test文件, 新增ready_data.py ,名字任意

import os
import random
import django

"""
运行该文件,需要把pycharm的运行模式换成unittest  (settings 中 tools 中  Python integrated tools)
该文件,像shell环境一样,可以操作模型,    会往数据库里添加数据
"""

# 设置环境变量
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "sq_tp.settings")  # sq_tp 表示项目名字
django.setup()

# 以下代码就像shell中一样使用
from project_interface.models import Projects_test, Interfaces_test

# 新增项目数据
p1 = Projects_test.objects.create(name='项目1', leader='小涛')
p2 = Projects_test.objects.create(name='项目2', leader='王爽')
print(p1,p2)

# 新增接口数据
i1= Interfaces_test.objects.create(name='接口1',projects=p1)
print(i1)

使用序列化器进行序列化输出、反序列化输入

安装并注册应用

安装pip install djangorestframework
pip install   markdown    浏览器显示的时候,支持markdown
注册时,要写到其他应用名之上?

要写到其他应用名之上?

定义序列化器

一般是在【应用】中新建py文件,推荐命名serializers.py
一般情况下, models.py中,一个模型类,定义一个序列化器,也可以定义多个

  • 要继承Serializer类
  • 类名字可以自定义
  • 每个字段名 命名要和模型类字段名 一致 (一般)

我们在练习时,就直接也在test文件中(或者应用下),新建一个serializers.py (暗示序列化器的定义实际上和模型关系不大

from rest_framework import serializers
# from rest_framework.validators import UniqueValidator
from .models import Projects_test

class TestSerilizer1(serializers.Serializer):
    name = serializers.CharField(label='项目名称', help_text='项目名称', max_length=20, min_length=5,
                                    # validators=[UniqueValidator(
                                 	# queryset=Projects_test.objects.all(),
                                  	# message="项目名称重复了00")
                                    # ]
                                 )
    leader = serializers.CharField(label='项目负责人', help_text='项目负责人', default='阿名')
    is_execute = serializers.BooleanField()
    # interface_set = serializers.PrimaryKeyRelatedField()


class TestSerilizer2(serializers.Serializer):
    name = serializers.CharField(label='项目名称', help_text='项目名称', max_length=20, min_length=5)
    # leader1 = serializers.CharField(label='项目负责人', help_text='项目负责人', default='阿名')
    is_execute = serializers.CharField()

class InterfacesSerilizer(serializers.Serializer):
    name = serializers.CharField(label='接口名称', help_text='项目名称')
    tester = serializers.CharField(label='测试人员', help_text='测试人员')

序列化器类字段_补充1 方法字段 SerializerMethodField

SerializerMethodField(), 参数为 method_name 固定的,后面为函数名称

  • 定义的xxx字段默认是只读的 (可以是对原来的覆盖,也可以新字段)
  • 通常:函数名称取成 get_xxx , 好看一点
class xxxSerializer(serializers.ModelSerializer):
	xxx = serializers.SerializerMethodField(method_name='函数名称')
	def 函数名称(self,instance):
		....
		return xxxx

举例:当一个模型序列化器返回的东西不够时,就可以这么增加一个字段,
比如数据库里 存的 性别sex 为 01, 我想多返回一个 sex_value ,就可以这样写,在函数里转化成 “男女”

使用序列化器(返回数据-序列化时)

写法
serializer= ProjectSerilizer(instance=queryset, many=True)
serializer = ProjectSerilizer(instance=Projects.objects.get(id=1))
那么:serializer.data 就是序列化后的数据

一般在views.py使用, 我们验证时,在test文件下新增一个out.py来验证

import os
import random
import django

# 设置环境变量
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "sq_tp.settings")  # sq_tp 表示项目名字
django.setup()

# 以下代码就像shell中一样使用
from project_interface.models import Projects_test, Interfaces_test

# 不使用序列化器,项目模型的所有数据都会返回
plist = Projects_test.objects.values()
print(plist)                                #  格式为列表套字典
print("------------------------------------------------------------------------------------")


# 使用序列化器,详情
from serializers import TestSerilizer1,TestSerilizer2
p_obj=Projects_test.objects.get(pk=1)
s1=TestSerilizer1(instance=p_obj)
s2=TestSerilizer2(instance=p_obj)
"""
s2.data -------特点1:没有leader数据------特点2:is_execut为字符串
"""
print(s1.data)
print(s2.data)
print("------------------------------------------------------------------------------------")


# 使用序列化器, 列表(要多加一个many参数)
s1 = TestSerilizer1(instance=plist,many=True)
print(s1.data)                              #  格式为列表套字典


序列化输出有两个特点(见下图文字说明)

  • 不使用序列化器,只能把模型的所有字段都返回
  • 如果使用的序列化器,可以选择性返回字段
  • 可以决定返回字段的类型

在这里插入图片描述
instance参数,如下参考图(这里是在views中定义了 两个视图函数,且配置关联了url、相关代码在最后

  • 如果是一个,就返回字典,
  • 如果是多个,就是列表套字典,
    在这里插入图片描述
    在这里插入图片描述

使用序列化器(新增数据-反序列化时)

在test文件下新增一个文件in.py来验证,依次传不同的body,感受一下反序列化的使用。

import os
import random
import django
"""
反序列化时的使用
1. 获取序列化器对象 s=TestSerilizer1(data=body)
2. 校验 s.is_valid()  # 校验成功返回True,校验失败返回False, 可以添加參數 raise_exception=True,校验失败时,会直接报错
3. is_valid() 为True时,获取校验后的数据 s.validated_data
4. is_valid() 为False时,获取校验失败的原因 s.errors
"""
# 设置环境变量
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "autotp_test1.settings")  # sq_tp 表示项目名字
django.setup()

# 以下代码就像shell中一样使用
from project_interface.models import Projects_test, Interfaces_test

# 不使用序列化器,新增时会,如果数据不对,会直接报错,就不举例了

# 使用序列化器,新增
from project_interface.serializers import TestSerilizer1,TestSerilizer2
body={"name":"名字长度符合要求的","leader":"小高","is_execute":True}
body1={"name":"名字长度符合要求的","leader":"小高","is_execute":True}  # 校验成功,但是新增会报错,因为name不能重复,序列化器name字段现在没有加重复的校验(后面可以用模型生成序列化器)
body2={"name":"名称短的","leader":"小高","is_execute":True}   # 校验失败,name长度不够
body3={"name":"名称符合要求呢","is_execute":True}             # 校验成功,  序列化器中,leader设置了默认值
body4={"name":"名称符合要求呢呀呀呀"}                          # 校验失败,序列化器中,is_execute没有设置默认值

"""本文档使用:依次修改body的值,查看校验结果"""
serializers_obj= TestSerilizer1(data=body2)

if serializers_obj.is_valid():  # 可以添加參數 raise_exception=True,校验失败时,会直接报错
    print("校验成功,开始新增")
    Projects_test.objects.create(**serializers_obj.validated_data)

    """説明,如过是body1时,虽然校验成功了,但是由于数据库里面已经有了name已经有了,所以会报错"""

else:
    print("校验失败,原因如下")
    print(serializers_obj.errors)

序列化器校验的作用
在这里插入图片描述
序列化器,判断body2时
要想知道有没有通过,还需要让序列化器做一个事情,即(要让它在已有的数据里判断一下)

就要再序列化器中加上对name重复的校验,看下面的validators的介绍, 如下:添加validators参数…

from rest_framework.validators import UniqueValidator
from project_interface.models import Projects_test
class TestSerilizer1(serializers.Serializer):
    name = serializers.CharField(label='项目名称', help_text='项目名称', max_length=20, min_length=5,
                                 validators=[UniqueValidator(
                                 	queryset=Projects_test.objects.all(),
                                  	message="项目名称重复了00")]
                                 )

在这里插入图片描述

补充:
serializers_obj.errors 的格式也是字典: 会把每个字段不通过的原因给出来:
如:
在这里插入图片描述

同样用接口举例,代码也在后面:
在这里插入图片描述
meige 都不写

接口举例的代码

1、project_interface应用的views.py中
(补充说明:APIView是django RF里最基础的视图类,里面可以重写get post等方法)

import json

from django.shortcuts import render

# Create your views here.
from rest_framework.views import APIView
from .models import Projects_test,Interfaces_test
from .serializers import TestSerilizer1,TestSerilizer2,InterfacesSerilizer     # 在project_interface 的应用下新建文件serializers,把它复制过来
from rest_framework.response import Response


class ProjectsView(APIView):
    def get(self,request,*args,**kwargs):
        s1 = TestSerilizer1(instance=Projects_test.objects.all(), many=True)
        print(s1.data)
        return Response(s1.data)

    def post(self, request):
        # 1、获取json参数并转化为python中的数据类型(字典)
        try:
            python_data = json.loads(request.body)
        except Exception as e:
            return Response({'msg': '参数有误'}, status=400)
        serializerObj = TestSerilizer1(data=python_data)
        if not serializerObj.is_valid():
            return Response(serializerObj.errors, status=400)
        # 新增
        Projects_test.objects.create(**serializerObj.validated_data)
        return Response(serializerObj.validated_data, status=201)

class ProjectsDetailView(APIView):
    def get(self,request,*args,**kwargs):
        p_obj = Projects_test.objects.get(pk=1)
        s1 = TestSerilizer1(instance=p_obj)
        return Response(s1.data)

    # 更新 略
    def put(self):
        pass


class InterfacesView(APIView):
    # 接口列表
    def get(self, request, *args, **kwargs):
        s1 = InterfacesSerilizer(instance=Interfaces_test.objects.all(), many=True)
        print(s1.data)
        return Response(s1.data)
	    # 新增接口
    def post(self, request):
        # 1、获取json参数并转化为python中的数据类型(字典)
        try:
            python_data = json.loads(request.body)
        except Exception as e:
            return Response({'msg': '参数有误'}, status=400)
        serializerObj = InterfacesSerilizer(data=python_data)
        if not serializerObj.is_valid():
            return Response(serializerObj.errors, status=400)
        # 新增
        interface_obj = Interfaces_test.objects.create(**serializerObj.validated_data)

        # 再使用一次序列化器返回
        serializerObj2 = InterfacesSerilizer(instance=interface_obj)
        return Response(serializerObj2.data, status=201)
#
class InterfacesDetailView(APIView):
    # 获取接口详情
    def get(self, request, *args, **kwargs):
        p_obj = Interfaces_test.objects.get(pk=1)
        s1 = InterfacesSerilizer(instance=p_obj)
        return Response(s1.data)


2、应用中urls.py

# 外层urls中 
path("",include('project_interface.urls'))

# 应用内新增urls.py,
from django.urls import path
from   project_interface import views

urlpatterns = [
    path("projects/", views.ProjectsView.as_view()),
    path("projects/<int:pk>/", views.ProjectsDetailView.as_view()),
    path("interfaces/", views.InterfacesView.as_view()),
    path("interfaces/<int:pk>/", views.InterfacesDetailView.as_view()),
]

3、把test中的serizlizer.py复制到应用中

一些校验规则、和一些参数说明

校验参数

原理(以下的校验规则可以逐个尝试):
一般:在序列化器中,写了哪些字段,就会校验哪些字段
在这里插入图片描述

  1. 默认必传,如果加上 required=False,就可以不传了, 如:可实现非必填,但填就要校验长度的需求

  2. 默认必传,如果加default参数,也可以不传且有默认值,serializerObj.validated_data,校验通过的数据,会拿到序列化器中定义的默认值
    在这里插入图片描述

  3. labelhelp_text 为描述信息,(暂不知道哪里会用到)

  4. max_valuemin_value 针对IntegerField 的 为最大值,和最小值 (没试过)

  5. max_lengthmin_length 为 CharField 的最长,和最短 (针对字符串)

  6. allow_null=True 请求的那个参数可以传 null
    在这里插入图片描述

  7. allow_blank=True # 允许为空字符串 ,基本同上

  8. write_only =Trueread_only =True

  • write_only =True 可以实现,只校验,但是不返回的需求

    • 接收前端传入的参数时(如新增、更新数据):

      • serializer_obj = CountrySerilizer(data=body) #data接收输入的数据
      • serializer_obj.is_valid() 判断时,会对输入做校验(判断长度是否正常,必填项有没有传),还是会对body中的每个字段进行校验
      • serializer_obj.validated_data 因为有校验,所以校验后,还是会有 校验后的这个字段。 校验后的data,中还是有该字段
    • 序列化输出时(返回数据,给前端, 查询全部、查询一个)

      • serialiser_obj=CountrySerilizer(instance=Country.objects.all(),many=True)
      • serialiser_obj=CountrySerilizer(instance=Country.objects.get(id=pk))
      • serializer_obj.data: 输出内容,无该字段。 输出内容里没有该字段
  • read_only =True 可以实现,不校验参数,但会返回的需求

    • 和上面相反。 1对请求参数不校验, 但是2 查询时会输出

功能参数

时间字段-序列化输出,时间格式化

可以加上format 参数,如:

update_time = serializers.DateTimeField(label='更新时间',help_text='更新时间',format='%Y年%m月%d日 %H:%M:%S' ,read_only=True)

在这里插入图片描述

补充:时间格式化 drf全局配置
REST_FRAMEWORK = {
    # 0、api返回时日期时间格式配置
    'DATETIME_FORMAT': "%Y-%m-%d %H:%M:%S",
    'DATE_FORMAT': "%Y-%m-%d",
    }

修改报错信息,加参数 error_messages

serializerObj.is_valid() 当他为False时, 报错信息是这样的
在这里插入图片描述

某个校验项: 重写的原因

name = serializers.CharField( max_length=20, min_length=5,
                                  error_messages={'min_length': '项目名称不能少于5位',
                                                 'max_length': '项目名称不能多余20位',
                                                 'required': "项目名称必填哈[这是重写的错误原因]"})

效果是这样的
在这里插入图片描述

获取关联表数据

项目表和 接口表 (1对多)的关系如下:
在这里插入图片描述

1234,每一种都是要加参数的,不然会报下面这个报错, 都需要传 这个两个参数的,(不能同时存在)
Relational field must provide a queryset argument, override get_queryset, or set read_only=True.

  • 参数1:queryset
    • 可以接受 请求参数去查 ,不想返回时,要加 read_only=False
    • 又可以返回
  • 参数2:read_only=True 表示 只做返回

举例(接口列表中展示项目)

  • 如返回的接口信息中要包含项目信息,如下图
    在这里插入图片描述
  • 要修改的 接口序列化器, – 定义字段时用models.py中 接口模型类中一样的字段名称,里面叫 projects

1、serializers.PrimaryKeyRelatedField()

·这个修改的是接口序列化器,加一个
projects = serializers.PrimaryKeyRelatedField(queryset=Projects_test.objects.all(),label='所属项目', help_text='所属项目',write_only=True)

输出的接口列表里包含有项目id,

当write_only=True 不写时,即为False, 响应里才展示 项目id
在这里插入图片描述

新增接口时,也需要传一个projects 参数,里面放id

在这里插入图片描述

2、StringRelatedField()

这种只能输出(即,可以实现接口列表,和 接口详情)
这种输出时: 返回的是,模型类中定义的  __str__  字符串
默认read_only = True了,所以这个就不用在指定了
"""再写一个序列化器,该序列化器用到 接口列表 和 接口详情"""
class InterfacesSerilizer2(serializers.Serializer):
    name = serializers.CharField(label='接口名称', help_text='项目名称')
    tester = serializers.CharField(label='测试人员', help_text='测试人员')
    # projects名称要和models中的一致,也叫projects
    projects = serializers.StringRelatedField()  # StringRelatedField 它的read_only默认就为True

views中,这个两个方法改一下,就有如下效果
在这里插入图片描述
在这里插入图片描述

3、SlugRelatedField()

·这个修改的是接口序列化器
输入和输出可以 用 slug_field 参数来指定一个 有唯一约束的字段名
必须指定read_only=True, or 指定queryset参数来接收输入

"""使用SlugRelatedField,自定义指定一个 项目表中的有唯一值的字段"""
class InterfacesSerilizer3(serializers.Serializer):
    name = serializers.CharField(label='接口名称', help_text='项目名称')
    tester = serializers.CharField(label='测试人员', help_text='测试人员')
    # projects名称要和models中的一致,也叫projects
    projects = serializers.SlugRelatedField(slug_field="name", queryset=Projects_test.objects.all(),label='所属项目', help_text='所属项目')

在这里插入图片描述

可以输出:

响应中的projects,就返回的是项目表的name
在这里插入图片描述

可以进行输入,请求输入时:

在这里插入图片描述

4、用关联表的序列化器

这种只能输出(接口列表,接口详情)
"""用关联表的序列化器"""
class InterfacesSerilizer4(serializers.Serializer):
    name = serializers.CharField(label='接口名称', help_text='项目名称')
    tester = serializers.CharField(label='测试人员', help_text='测试人员')
    # projects名称要和models中的一致,也叫projects
    projects = TestSerilizer1(read_only=True)

在这里插入图片描述
在这里插入图片描述
项目字段里要展示哪些东西,也可以进行设置
在这里插入图片描述

这种也可以做新增

写法如下
在这里插入图片描述
页面上和接口是这种,即在新建用例时,需要填写config的信息
在这里插入图片描述
用例新增和用例列表的效果如下
在这里插入图片描述

举例(项目列表中展示接口)

  • 如果是项目列表,就要修改项目序列化器,–定义字段时表名 – 用关联模型小写名_set这里是interfaces_set 字段, 当然也可以用在定义外键时定义的related_name,如下图
  • 因为接口 的数据时,多方, 在定义时,下方的1234,都要写many =True
    在这里插入图片描述

1、同上,效果如图

基本相同,就是名称需要是 接口模型小写名_set 或者related_name
在这里插入图片描述

2、同上,效果如下

在这里插入图片描述

3、同上,效果如下

在这里插入图片描述

4、同上,效果如下

在这里插入图片描述

额外的校验

validators

validators中结合模型类的校验(校验某字段是否重复)

如项目表中name字段有唯一约束, 怎么把唯一约束放到序列化器中,做校验
添加validator 参数. 创建校验器
如下: 会去判断 Projects模型类中的name 是否有重复的, 如果有,就把message
UniqueValidator 中,必须要指定 queryset参数。

from rest_framework.validators import UniqueValidator
  name = serializers.CharField(label='项目名称',
                                 validators=[UniqueValidator(
                                 	queryset=Projects.objects.all(),
                                  	message="项目名称重复了00"
                                 )])

在这里插入图片描述

validators 中使用自定义校验方法

举例: 如在新增项目名时,假设 名字需要 后两位是 ”项目“ 结尾
自定义一个方法,然后把方法名写在validators=[], 后面的列表中,方法名后不打括号
在自定义方法中,

  • 第一个参数为待校验的值,写法如下,
  • 需要有抛出 serializers.ValidationError 的地方, 且抛的异常只能是这种。
  • 方法名 是 放在validators 中的 列表参数里面
# 自定义方法
def is_contain_keyword(value):
    if '项目' not in value:
        raise serializers.ValidationError('项目名称中必须要包含“项目”')   # 必须是抛出这种异常

# 序列化器中使用
    name = serializers.CharField(label='项目名称', help_text='项目名称', max_length=20, min_length=5,
                                 error_messages={'min_length': '项目名称不能少于5位',
                                                 'max_length': '项目名称不能多余20位',
                                                 'required': "项目名称必填哈[这是重写的错误原因]"},
                                 validators=[UniqueValidator(queryset=Projects.objects.all(), message="项目名称重复了00"),
                                             is_contain_keyword]	# 方法名不用打括号
                               )

在这里插入图片描述

validate_name()单字段校验方法

这个定义在序列化器中
如def validate_name() 方法,首先会去校验如下的 name是否有不通过项,如果没有才走这里的代码
在这里插入图片描述
特点

  • 方法名为validate_字段名
  • 第一参数为,待校验的值
  • 最后需要返回 待校验的值 (往往需要?)
  • 上面的通过了, 代码才会走到 validate_name 方法这里, 如果上面有一个不通过,就不会走这里单字段校验方法
  • 也需要有抛出 serializers.ValidationError 的地方, 且抛的异常只能是这种
    def validate_name(self,attr:str):
        # 对name再次进行校验  (name定义的其他校验项通过,才走这里)
        if not attr.endswith('项目'):
            raise serializers.ValidationError("项目名称中,必须以“项目 结尾")
        return attr

validate() 多字段进行联合校验

定义这种方法,

  • 方法名是固定的
  • 参数是字典的形式,可以获取到每个字段的数据,然后进行判断, 最后需要返回
  • 也是需要抛异常
  • 这个方法,是 单字段校验方法,后再校验的
    def validate(self,attrs):
        attrs.get('leader')
        attrs.get('name')
        pass		# 判断每个字段长度 等等
        return attrs

校验的总结 and 校验顺序

  1. 单个字段中:先校验它的【validator列表中的校验规则】, 上面的validators部分
  2. 再校验【本身的校验参数如max_length=20】等
  3. 【单字段校验】方法 def validate_name()
  4. 【多字段联合校验】方法 def validate()
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值