前言

   ModelSerializer中还具有一些高级用法,如批量更新、批量删除、批量创建等。

   但是批量过来的数据格式都需要与前端做好协商,什么样的数据格式是单条操作,什么样的数据格式是批量操作。

   如下,对于单条操作而言,只需要传入编号即可,而批量操作则需要在请求体中传入[]以及被操作的主键编号。

模型表

   书籍表、出版社表、作者表、作者详情表

   作者和作者详情表是一对一关系

   书籍和出版社表是一对多关系

   书籍和作者是多对多关系

   以上关系均是为ORM查询方便所提供,但是在物理表中并没有确切关联

   由于这些表中的数据具有商业价值,所以我们要给它设定一个字段名为delete_status,代表是否逻辑删除(真实并不会删除)。

   初此之外还有create_time代表该记录的创建时间,以及last_update_time代表这张表的最后更新时间。

   所以我们可以给这三张字段抽出一张抽象表,用于被其他表继承:

from django.db import models

# Create your models here.
class BaseModel(models.Model):
delete_status = models.BooleanField(default=False) # 默认不删除
create_time = models.DateTimeField(auto_now_add=True) # 新增记录时自动插入
last_update_time = models.DateTimeField(auto_now=True) # 更新记录时自动插入 class Meta:
abstract = True # 抽象类,不会创建真实表,用于继承 class Book(BaseModel):
book_id = models.AutoField(primary_key=True)
book_name = models.CharField(max_length=32,verbose_name="书籍名称")
book_price = models.DecimalField(max_digits=5,decimal_places=2,verbose_name="书籍价格")
publish = models.ForeignKey(to="Publish",on_delete=models.DO_NOTHING,db_constraint=False)
# 逻辑一对多,实际表没有关联,删除出版社书不受影响
authors = models.ManyToManyField(to="Author",db_constraint=False)
# 逻辑多对多,实际表没有任何关联
class Meta:
verbose_name_plural = "书籍表" def __str__(self):
return self.book_name @property
def publish_name(self): # 模拟字段,用于序列化时显示出版社名称
return self.publish.publish_name @property
def author_list(self): # 模拟字段,用于序列化时显示多个作者的名字和性别
author_list = self.authors.all() return [
{"author_name":author.author_name,"author_gender":author.get_author_gender_display()} for author in author_list
] class Publish(BaseModel):
publish_id = models.AutoField(primary_key=True)
publish_name = models.CharField(max_length=32,verbose_name="出版社名称")
publish_addr = models.CharField(max_length=32,verbose_name="出版社地址") def __str__(self):
return self.publish_name class Author(BaseModel):
author_id = models.AutoField(primary_key=True)
author_name = models.CharField(max_length=32,verbose_name="作者姓名")
author_gender = models.IntegerField(choices=[(1,"男"),(2,"女")])
author_detail = models.OneToOneField(to="AuthorDetail",db_constraint=False,on_delete=models.CASCADE)
# 逻辑一对一,实际上没有什么联系 class AuthorDetail(BaseModel):
author_phone = models.CharField(max_length=11) # 二、表断关联
# 1、表之间没有外键关联,但是有外键逻辑关联(有充当外键的字段)
# 2、断关联后不会影响数据库查询效率,但是会极大提高数据库增删改效率(不影响增删改查操作)
# 3、断关联一定要通过逻辑保证表之间数据的安全,不要出现脏数据,代码控制
# 4、断关联
# 5、级联关系
# 作者没了,详情也没:on_delete=models.CASCADE
# 出版社没了,书还是那个出版社出版:on_delete=models.DO_NOTHING
# 部门没了,员工没有部门(空不能):null=True, on_delete=models.SET_NULL
# 部门没了,员工进入默认部门(默认值):default=0, on_delete=models.SET_DEFAULT

详解序列器

   进行序列化时,如何区分是创建、更新以及查询单条或多条呢?这其实涉及到ModelSerializer的参数问题。

def __init__(self, instance=None, data=empty, **kwargs):

   如果只传入data参数,代表这是新增一条,则情况如下:

方法\属性\钩子 状态
is_valid() 可用
initial_data() 可用
validated_data 仅在调用is_valid()之后可用
errors 仅在调用is_valid()之后可用
data 仅在调用is_valid()之后可用

   如果没有传递data参数,则会发生如下情况:

方法\属性\钩子 状态
is_valid() 不可用
initial_data() 不可用
validated_data 不可用
errors 不可用
data 可用

   我们要注意传递参数的情况如下:

   查询单条,只会传入instance

   创建一条,只会传入data

   更新一条,会传入instance以及data

   上面没有涉及到查询多条,那么在查询多条时我们会传递进many参数,在内部会执行这样一段代码:

    def __new__(cls, *args, **kwargs):
# We override this method in order to automatically create
# `ListSerializer` classes instead when `many=True` is set.
if kwargs.pop('many', False):
return cls.many_init(*args, **kwargs)
return super().__new__(cls, *args, **kwargs)

   这句代码的意思是,当有manyTrue时代表这是多条操作,它将不会实例化你的自定义序列化器,因为你的自定义序列化器都是针对单条记录的,转而它会实例化一个叫做ListSerializer的类,该类是内置的,并且该类支持批量创建。

   下面是ListSerializer的部分源码,其中child代表你自定义的序列化器:

class ListSerializer(BaseSerializer):
child = None # 代表你自己写的序列化器
many = True default_error_messages = {
'not_a_list': _('Expected a list of items but got type "{input_type}".'),
'empty': _('This list may not be empty.')
} def __init__(self, *args, **kwargs):
self.child = kwargs.pop('child', copy.deepcopy(self.child)) # 自己写的序列化器
self.allow_empty = kwargs.pop('allow_empty', True)
assert self.child is not None, '`child` is a required argument.'
assert not inspect.isclass(self.child), '`child` has not been instantiated.'
super().__init__(*args, **kwargs)
self.child.bind(field_name='', parent=self)

   继续向下看它的源码,可以发现它是支持批量创建的,但是不支持批量更新。

    def update(self, instance, validated_data):
raise NotImplementedError(
"Serializers with many=True do not support multiple update by "
"default, only multiple create. For updates it is unclear how to "
"deal with insertions and deletions. If you need to support "
"multiple update, use a `ListSerializer` class and override "
"`.update()` so you can specify the behavior exactly."
) def create(self, validated_data):
return [
self.child.create(attrs) for attrs in validated_data
]

   如果我们对自己的序列化器做一个ListSerializer,则可以继承原生的ListSerializer,并且指定好当有many参数传递时,实例化的是我们自己写的ListSerializer即可。如下所示:

from rest_framework.serializers import ModelSerializer
from rest_framework.serializers import ListSerializer
from app01 import models # 写一个类,继承ListSerializer,重写update方法实现批量更新
class BookListSerializer(ListSerializer):
def update(self,instance,validated_data): return [
self.child.update(instance[i],attrs) for i,attrs in enumerate(validated_data)
] class BookModelSerializer(ModelSerializer):
class Meta:
list_serializer_class = BookListSerializer # 使用many参数后后用指定的类进行实例化
model = models.Book # depth=0 # 序列化的关联层级
fields = ("pk","book_name","book_price","authors","publish","publish_name","author_list")
# 使用property来拿到展示的数据 extra_kwargs = {
"publish":{"write_only":True}, # 不展示,但是新增或更新需要指定
"publish_name":{"read_only":True}, # 仅用于展示
"authors":{"write_only":True},
"author_list":{"read_only":True},
}

   总结如下:

   1.我们自己写的序列化器都只能对单条进行操作

   2.如果传入many为True时,则会自动序列化一个内部的ListSerializer类,它支持批量创建多条,但是不支持批量更新

   3.在自定义序列器中使用list_serializer_class类属性即可指定在进行批量操纵时用哪一个类进行实例化

序列化与反序列化

   查看上面的代码,你可以发现在model中的很多地方都用了property来对实例方法进行装饰。

   它其实是为了配合序列化做的,因为我们在自定以序列化器中的fields属性里将他们进行加入了,如下所示:

    @property
def publish_name(self): # 模拟字段,用于序列化时显示出版社名称
return self.publish.publish_name @property
def author_list(self): # 模拟字段,用于序列化时显示多个作者的名字和性别
author_list = self.authors.all() return [
{"author_name":author.author_name,"author_gender":author.get_author_gender_display()} for author in author_list
] fields = ("pk","book_name","book_price","authors","publish","publish_name","author_list") # 返回外键,出版社的名字,返回多对多外键,作者的列表

   并且,还指定了extra_kwargs参数,用于指定哪些参数是序列化时用,哪些参数是反序列化时用:

        extra_kwargs = {
"publish":{"write_only":True}, # 不展示,但是新增或更新需要指定
"publish_name":{"read_only":True}, # 仅用于展示
"authors":{"write_only":True},
"author_list":{"read_only":True},
}

   这其实也要与前端沟通好,我序列化丢给你的数据是字符串,但是你要创建时必须给我把诸如出版社、作者等信息的pk放进来才行。

   大概意思就是,我丢给你字符串让用户看,你创建或更新时我不要字符串,我要pk主键。

   此外,还有一个参数叫做depth,它规定了在序列化时是否连同外键一起进行序列化,并且序列化的层级是多少,一般看一看就行了。

接口书写

   规定,对于单条操作,直接放在?请求地址后面。放入pk即可。

   多条操作,你需要放入请求body中,以[]形式进行传递。

from rest_framework.generics import GenericAPIView
from rest_framework.mixins import CreateModelMixin,ListModelMixin,RetrieveModelMixin,UpdateModelMixin
from rest_framework.response import Response from app01.serializer import *
from app01.models import * class BookAPI(GenericAPIView,CreateModelMixin,ListModelMixin,RetrieveModelMixin,UpdateModelMixin):
queryset = Book.objects.filter(delete_status=False) # 查询未删除的数据
serializer_class = BookModelSerializer def get(self,request,*args,**kwargs):
pk = kwargs.get("pk")
if not pk: # 获取所有
return self.list(request)
# 获取单条
return self.retrieve(request,pk) def post(self,request,*args,**kwargs):
# 新增一条
if isinstance(request.data,dict):
return self.create(request) # 自动返回 # 新增多条
elif isinstance(request.data,list):
# 现在执行我们自己定义的ListSerializer,因为传入many=True.由于继承原生的ListSerializer,它自己有create方法
book_ser = self.get_serializer(data=request.data,many=True)
book_ser.is_valid(raise_exception=True) # 序列化失败直接抛出异常
book_ser.save()
return Response(data=book_ser.data) def patch(self, request, *args, **kwargs):
pk = kwargs.get("pk")
if not pk:
return self.update(request,pk)
# 改多个
# 前端传递数据格式[{book_id:1,book_name:xx,book_price:xx},{book_id:1,book_name:xx,book_price:xx}]
# 处理传入的数据 对象列表[book1,book2] 修改的数据列表[{book_name:xx,book_price:xx},{book_name:xx,book_price:xx}]
book_list = []
modify_data = []
for item in request.data:
# {book_id:1,book_name:xx,book_price:xx} 取出pk,不允许修改pk
pk = item.pop("book_id")
book_obj = models.Book.objects.get(pk=pk)
book_list.append(book_obj)
modify_data.append(item) book_ser = BookModelSerializer(instance=book_list,data=modify_data,many=True,partial=True) # parital允许局部修改,这个主要针对put,patch本身就是True
# 处理时:
# self.child.update(instance[i],attrs) for i,attrs in enumerate(validated_data) 传入id,和要修改的数据
book_ser.is_valid(raise_exception=True)
book_ser.save()
return Response(book_ser.data) def delete(self,request,*args,**kwargs):
pk = kwargs.get("pk")
pks = [] # 用于获取要删除的id,全部放入列表中
if pk:
# 删一个
pks.append(pk)
else:
pks = request.data.get("pks") result = models.Book.objects.filter(pk__in=pks,delete_status=False).update(delete_status=True)
if result:
return Response(data="删除%s条记录成功"%len(pks))
else:
return Response(data="删除失败,没有要删除的数据")

数据格式

   新增数据:

# 新增一条 http://127.0.0.1:8000/api/books/  POST请求
# 请求的数据格式: {
"book_name":"新书,单条",
"book_price": "123.00",
"publish": 1,
"authors": [
1,2
]
} # 返回格式: {
"pk": 12,
"book_name": "新书,单条",
"book_price": "123.00",
"publish_name": "北京出版社",
"author_list": [
{
"author_name": "云崖",
"author_gender": "男"
},
{
"author_name": "小屁孩",
"author_gender": "女"
}
]
} # 新增多条 http://127.0.0.1:8000/api/books/ POST请求
# 请求的数据格式: [
{
"book_name":"新书1,多条",
"book_price": "88.00",
"publish": 1,
"authors": [
1
]
},
{
"book_name":"新书2,多条",
"book_price": "13.00",
"publish": 1,
"authors": [
2,3
]
},
{
"book_name":"新书3,多条",
"book_price": "63.00",
"publish": 1,
"authors": [
3,4
]
}
] # 返回格式: [
{
"pk": 18,
"book_name": "新书1,多条",
"book_price": "88.00",
"publish_name": "北京出版社",
"author_list": [
{
"author_name": "云崖",
"author_gender": "男"
}
]
},
{
"pk": 19,
"book_name": "新书2,多条",
"book_price": "13.00",
"publish_name": "北京出版社",
"author_list": [
{
"author_name": "小屁孩",
"author_gender": "女"
},
{
"author_name": "东仙人",
"author_gender": "男"
}
]
},
{
"pk": 20,
"book_name": "新书3,多条",
"book_price": "63.00",
"publish_name": "北京出版社",
"author_list": [
{
"author_name": "东仙人",
"author_gender": "男"
},
{
"author_name": "云散人",
"author_gender": "女"
}
]
}
]

   修改数据:

# 修改一条 http://127.0.0.1:8000/api/books/1/  patch请求
# 请求的数据格式:
{
"book_name":"修改新书",
"book_price": "123.00",
"publish": 3,
"authors": [
2,3
]
} # 返回格式:
{
"pk": 1,
"book_name": "修改新书",
"book_price": "123.00",
"publish_name": "西京出版社",
"author_list": [
{
"author_name": "小屁孩",
"author_gender": "女"
},
{
"author_name": "东仙人",
"author_gender": "男"
}
]
} # 修改多条 http://127.0.0.1:8000/api/books/ patch请求
# 请求的数据格式:
[
{
"pk": 1,
"book_name":"修改新书",
"book_price": "123.00",
"publish": 1,
"authors": [
3,4
]
},
{
"pk": 2,
"book_name":"修改新书2",
"book_price": "123.00",
"publish": 1,
"authors": [
1,2
]
}
] # 返回格式:
[
{
"pk": 1,
"book_name": "修改新书",
"book_price": "123.00",
"publish_name": "北京出版社",
"author_list": [
{
"author_name": "东仙人",
"author_gender": "男"
},
{
"author_name": "云散人",
"author_gender": "女"
}
]
},
{
"pk": 2,
"book_name": "修改新书2",
"book_price": "123.00",
"publish_name": "北京出版社",
"author_list": [
{
"author_name": "云崖",
"author_gender": "男"
},
{
"author_name": "小屁孩",
"author_gender": "女"
}
]
}
]

   删除数据:

# 删除一条 http://127.0.0.1:8000/api/books/1/  delete请求

# 删除多条 http://127.0.0.1:8000/api/books/  delete请求
# 请求数据格式:
{
"pks":
[1,2,3]
}

ModelSerializer 高级使用的更多相关文章

  1. drf框架 - 序列化组件 | ModelSerializer (查,增,删,改)

    ModelSerializer 序列化准备: 配置 settings.py # 注册rest_framework框架 INSTALLED_APPS = [ ... 'rest_framework' ] ...

  2. drf序列化高级、自定义只读只写、序列化覆盖字段、二次封装Response、数据库查询优化(断关联)、十大接口、视图家族

    目录 自定义只读 自定义只写 序列化覆盖字段 二次封装Response 数据库关系分析 断外键关联关系 ORM操作外键关系 ORM四种关联关系 基表 系列化类其他配置(了解) 十大接口 BaseSer ...

  3. 4)drf序列化组件 Serializer(偏底层)、ModelSerializer(重点)、ListModelSerializer(辅助群改)

    知识点:Serializer(偏底层).ModelSerializer(重点).ListModelSerializer(辅助群改) 一.Serializer 偏底层 一般不用 理解原理 1.序列化准备 ...

  4. 序列化之Serializer类与ModelSerializer类的使用

    序列化之Serializer类的使用(5星) 作用: 序列化,序列化器会把模型对象转换成字典,经过response以后变成json字符串 反序列化,把客户端发送过来的数据,经过request以后变成字 ...

  5. MySQL高级知识- MySQL的架构介绍

    [TOC] 1.MySQL 简介 概述 MySQL是一个关系型数据库管理系统,由瑞典MySQL AB公司开发,目前属于Oracle公司. MySQL是一种关联数据库管理系统,将数据保存在不同的表中,而 ...

  6. PayPal高级工程总监:读完这100篇论文 就能成大数据高手(附论文下载)

    100 open source Big Data architecture papers for data professionals. 读完这100篇论文 就能成大数据高手 作者 白宁超 2016年 ...

  7. 马哥linux运维初级+中级+高级 视频教程 教学视频 全套下载(近50G)

    马哥linux运维初级+中级+高级 视频教程 教学视频 全套下载(近50G)目录详情:18_02_ssl协议.openssl及创建私有CA18_03_OpenSSH服务及其相关应用09_01_磁盘及文 ...

  8. JS高级前端开发群加群说明及如何晋级

    JS高级前端开发群加群说明 一.文章背景: 二. 高级群: 三. 加入方式: 四. 说明:   一.文章背景: 去年年初建了几个群,在不经意间火了,一直排在“前端开发”关键字搜索结果第一名.当然取得这 ...

  9. C#高级知识点&(ABP框架理论学习高级篇)——白金版

    前言摘要 很早以前就有要写ABP高级系列教程的计划了,但是迟迟到现在这个高级理论系列才和大家见面.其实这篇博客很早就着手写了,只是楼主一直写写停停.看看下图,就知道这篇博客的生产日期了,谁知它的出厂日 ...

随机推荐

  1. CUP的MESI协议

    MESI协议中的状态 CPU中每个缓存行(caceh line)使用4种状态进行标记(使用额外的两位(bit)表示): M: 被修改(Modified) 该缓存行只被缓存在该CPU的缓存中,并且是被修 ...

  2. 035 01 Android 零基础入门 01 Java基础语法 04 Java流程控制之选择结构 02 多重if结构

    035 01 Android 零基础入门 01 Java基础语法 04 Java流程控制之选择结构 02 多重if结构 本文知识点:Java中的多重if结构 选择结构回顾 if选择结构 注意: 1.条 ...

  3. 一键安装PyCharm

    1.准备阶段,首先去官网下载:https://download.jetbrains.8686c.com/python/pycharm-professional-2019.1.exe 官网地址:http ...

  4. 3-kubernetes监控与日志管理

    监控集群资源利用率 metrics-server是一个集群范围的资源使用情况的数据聚合器,作为一个应用部署在集群中 metrics-server从每个节点上kubelet API收集指标,通过kube ...

  5. 部署docker swarm集群

    基础环境 机器名称 IP地址 安装的软件 node-1 192.168.10.190 docker-ce node-2 192.168.10.191 docker-ce node-3 192.168. ...

  6. vue 异步提交php 两种方式传值

    1.首先要在php的入口文件写上一条代码,允许异步提交 header("ACCESS-CONTROL-ALLOW-ORIGIN:*"); 2.在vue有两种方式将数据异步提交到ph ...

  7. kubernetes-介绍与特性

    1. kubernetes概述 1) kubernetes是什么 2) kubernetes能做什么 3) kubernetes特性 4) kubernetes集群架构与组件 5) kubernete ...

  8. protoc-c 安装记录

    记录下 protobuf-c 安装过程中的问题. 1) 安装的时候没细看依赖. --  protobuf-c requires a C compiler, a C++ compiler, protob ...

  9. Shell Scripting 笔记

    Shell Scripting Tutorial Variables in the Bourne shell do not have to be declared, as they do in lan ...

  10. Archery安装教程

    一. CentOS设置 1. 更换阿里源 curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos ...