drf--ModelSerializers序列化

项目准备

配置 settings.py

# 注册rest_framework app
INSTALLED_APPS = [
# ...
'rest_framework',
] # 连接mysql数据库
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'dg_proj',
'USER': 'root',
'PASSWORD': '123',
'HOST': '127.0.0.1',
'PORT': 3306
}
} # 导入pymysql模块
"""
任何__init__文件
import pymysql
pymysql.install_as_MySQLdb()
""" # 汉化
LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai'
USE_I18N = True
USE_L10N = True
USE_TZ = False # 配置静态文件路径以及媒体文件路径
STATIC_URL = '/static/'
STATICFILES_DIRS = (
os.path.join(BASE_DIR, 'static'),
) MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

路由

主路由 urls.py

from django.conf.urls import url, include
from django.contib import admin
from django.views import serve
from django.conf import settings urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^api/', include('api.urls')),
url(r'^media/(?P<path>.*)', serve, {'document_root': settings.MEDIA_ROOT}),
]

子路由 api/urls.py

from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^books/$', views.BookAPIView.as_view()),
url(r'^books/(?P<pk>\d+)/$', views.BookAPIView.as_view()),
]

多表设计

表关系分析

"""
Book表:
name, price, img, publish(关联Publish表), authors(关联Author表), is_delete, create_time Publish表:
name, address, is_delete, create_time Author表:
name, age, is_delete, create_time AuthorDetail表:
mobile, author(关联Author表), is_delete, create_time 因为每个字段都有is_delete, create_time这两个字段,所以我们可以设置一个基表,其他的表继承基表
BaseModel表:
is_delete, create_time
"""

**表关系图: **

创建models

基表 utils/models.py

from django.db import models

class BaseModel(models.Model):
is_delete = models.BooleanField(default=False)
create_time = models.DateTimeField(auto_now_add=True, null=True) class Meta:
# 抽象表, 不会完成数据库迁移
abstract = True

模型层 api/models.py

from django.db import models
from utils.models import BaseModel
"""
relate_name: 从表在查主表时,可以通过relate_name进行查询,无需再进行反向查询(表名小写_set)
db_constraint: 设为False表示断开表关系
on_delete: 默认为CASCSADE, 表示主表进行删除操作时进行级联删除
DO_NOTHING, 表示主表进行删除时, 不进行任何操作
SET_NULL, 表示主表进行删除时, 该字段设为null, 此时null要设为True
SET_DEFAULT, 表示主表进行删除时, 该字段设为默认值, 此时default要进行设置 注意: on_delete操作, 从表删除时,主表不会有任何变化
db_table: 设置建表是的表名, 不采用默认表名
verbose_name: 在admin中进行操作时, 反馈到前台表或者字段的名字
""" # Create your models here.
class Book(BaseModel):
name = models.CharField(max_length=64)
price = models.DecimalField(max_digits=6, decimal_places=2)
img = models.ImageField(upload_to='icon', default='icon/default.png')
publish = models.ForeignKey(to='Publish',
null=True,
related_name='books',
db_constraint=False,
on_delete=models.DO_NOTHING
# on_delete=models.SET_NULL, null=True
# on_delete=models.SET_DEFAULT, default=1
)
authors = models.ManyToManyField(to='Author',
related_name='authors',
db_constraint=False
)
# 自定义model类的方法,完成插拔式跨表查询,默认在校验时为read_only
@property
def publish_info(self):
return {'name': self.publish.name, 'address': self.publish.address} @property
def author_info(self):
author_list = [] for author in self.authors.all():
detail = AuthorDetail.objects.filter(author_id=self.author.id)
author_list.append({
'name': author.name,
'age': author.age,
# 注意点:当author.detail为空时,就直接不走这一步了
# 'mobile': author.detail.mobile
'mobile': '未知' if not detail else author.detail.mobile
}) return author_list class Meta:
db_table = "books"
verbose_name = '书籍'
verbose_name_plural = verbose_name def __str__(self):
return self.name class Author(BaseModel):
name = models.CharField(max_length=64)
age = models.IntegerField() @property
def mobile(self):
return self.detail.mobile class Meta:
db_table = 'author'
verbose_name = '作者'
verbose_name_plural = verbose_name def __str__(self):
return self.name class AuthorDetail(BaseModel):
mobile = models.CharField(max_length=11)
author = models.OneToOneField(to='Author',
null=True,
related_name='detail',
db_constraint=False,
on_delete=models.CASCADE
)
class Meta:
db_table = 'detail'
verbose_name = '作者详情'
verbose_name_plural = verbose_name def __str__(self):
return f"{self.author.name}的详情" class Publish(BaseModel):
name = models.CharField(max_length=64)
address = models.CharField(max_length=128) class Meta:
db_table = 'publish'
verbose_name = '出版社'
verbose_name_plural = verbose_name def __str__(self):
return self.name

模型序列化

继承自rest_framework.serializers中的ModelSerializer

  1. 在自定义的ModelSerializer类中设置class Meta

    model 绑定序列化的模型类

    fields 通过插拔的方式指定序列化字段

  2. 在模型类中,通过在模型表方法属性来自定义连表查询的字段,然后在fields中插拔

  3. 如果使用外键字段完成连表查询,用序列化深度

    eg:publish = PublishModelSerializer()

自定义模型序列化 api/serializers.py

from rest_framework import serializers
from . import models # 序列化深度
class PublishModelSerializer(serializers.ModelSerializer):
class Meta:
model = models.Publish
fields = ('name', 'address') # 序列化深度
class AuthorModelSerializer(serializers.ModelSerializer):
class Meta:
model = models.Author
fields = ('name', 'age', 'mobile') class BookModelSerializer(serializers.ModelSerializer):
# 正向序列化深度
publish = PublishModelSerializer()
authors = AuthorModelSerializer(many=True) """了解:
在ModelSerializer中不建议使用,如何书写了必须在fields中声明使用
p_n = serializers.SerializerMethodField()
def get_p_n(self, obj: models.Book):
return obj.publish.name
"""
class Meta:
# 绑定序列化的模型类
model = models.Book
# 插拔字段
fields = ('name', 'price', 'img', 'publish', 'authors', 'publish_info', 'author_info') """了解:
fields = "__all__" :拿出所有字段
exclude = ('id', 'is_delete'):除了这些字段,其它字段全部拿出来
depth = 1 :跨表查询的深度(展示外键的所有字段)
"""

视图中使用 api/views.py

from rest_framework.views import APIView
from utils.response import APIResponse
from . import models, serializers class BookAPIView(APIView):
def get(self, request, *args, **kwargs):
# 单取
pk = kwargs.get('pk')
if pk:
book_obj = models.Book.objects.filter(pk=pk, is_delete=False).first()
if not book_obj:
return APIResponse(1, 'pk有误') book_ser = serializers.BookModelSerializer(book_obj)
return APIResponse(0, 'ok', results=book_ser.data)
book_obj_list = models.Book.objects.filter(is_delete=False).all()
if not book_obj_list:
return APIResponse(1,'没有数据')
book_ser = serializers.BookModelSerializer(book_obj_list, many=True)
return APIResponse(0,'ok',results=book_ser.data)

模型反序列化

继承自rest_framework.serializers中的ModelSerializer

  1. 在自定义的ModelSerializer类中设置class Meta

    models:绑定反序列化相关的模型类

    fields:插拔方式指定反序列化字段

    extra_kwargs:定义系统校验字段的规则

  2. 可以自定义局部钩子和全局钩子完成字段的复杂校验规则

  3. 不需要重写create和update完成增加修改,ModelSerializer类已经帮我们实现了

自定义模型反序列化 api/serializers.py

from rest_framework import serializers
from . import models class BookModelDeserializer(serializers.ModelSerializer):
# 自定义的校验字段,一定要插入到fields中
re_name = serializers.CharField(
min_length=3,
required=True,
error_messages={
'min_length': '太短了',
'required': '不能为空'
}
)
class Meta:
model = models.Book
fields = ('name', 're_name', 'price', 'publish', 'authors')
extra_kwargs = {
'name':{
'min_length': 3,
'error_messages': {
'min_length': '太短了',
'required': '不能为空'
}
},
# 有默认值的字段会默认required为False,在反序列化中就不会进行校验
'publish':{
'required': True,
'error_messages':{
'required': '不能为空'
}
},
'authors':{
'required': True,
'error_messages':{
'required': '不能为空'
}
}
} # 自定义校验规则
# 局部钩子
def validate_name(self, value):
if 'sb' in value:
raise serializers.ValidationError('书名包含敏感词汇')
return value def validate(self, attr):
name = attr.get('name')
re_name = attr.get('re_name')
publish = attr.get('publish')
if name != re_name:
raise serializers.ValidationError(
{'re_name': '两次书名不一致'}
)
if models.Book.objects.filter(name=name, publish=publish):
raise serializers.ValidationError(
{'book': '书籍已存在'}
) return attr

视图中使用 api/views.py

from rest_framework.views import APIView
from utils.response import APIResponse
from . import models, serializers class BookAPIView(APIView):
def post(self, request, *args, **kwargs):
book_ser = serializers.BookModelDeserializer(data=request.data)
if book_ser.is_valid():
book_obj = book_ser.save()
results = serializers.BookModelDeserializer(book_obj).data
return APIResponse(0, 'ok', results=results)
else:
return APIResponse(1, '添加失败', resultes=book_ser.errors)

模型序列化器(序列化与反序列化整合*****

子路由 api/urls.py

from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^v2/books/$', views.BookV2APIView.as_view()),
url(r'^v2/books/(?P<pk>.*)/$', views.BookV2APIView.as_view()),
]

自定义序列化器 api/serializers.py

from rest_framework import serializers
from . import models class BookV2ModelSerializer(serializers.ModelSerializer):
re_name = serializers.CharField(
min_length=3,
required=True,
write_only=True, # 只参与反序列化
error_messages={
'min_length': '太短了',
'required': '不能为空'
}
) class Meta:
model = models.Book
fields = ('name', 're_name', 'price', 'img', 'publish', 'publish_info', 'authors', 'authors_info')
extra_kwargs = {
'name':{
'min_length': 3,
'error_messages': {
'min_length': '太短了',
'required': '不能为空'
}
},
# 有默认值的字段会默认required为False,在反序列化中如果不传值不会进行校验,但是如果传值就会进行校验
'publish':{
'required': True,
'write_only': True,
'error_messages':{
'required': '不能为空'
}
},
'authors':{
'required': True,
'write_only': True,
'error_messages':{
'required': '不能为空'
}
},
} # 自定义校验规则
# 局部钩子
def validate_name(self, value):
if 'sb' in value:
raise serializers.ValidationError('书名包含敏感词汇')
return value def validate(self, attr):
name = attr.get('name')
re_name = attr.get('re_name')
publish = attr.get('publish')
if name != re_name:
raise serializers.ValidationError(
{'re_name': '两次书名不一致'}
)
if models.Book.objects.filter(name=name, publish=publish):
raise serializers.ValidationError(
{'book': '书籍已存在'}
) return attr

视图中使用 api/views.py

class BookV2APIView(APIView):
def get(self, request, *args, **kwargs):
pk = kwargs.get('pk')
# 单取
if pk:
book_obj = models.Book.objects.filter(is_delete=False, pk=pk).first()
if not pk:
return APIResponse(1, 'pk有误')
book_ser = serializers.BookV2ModelSerializer(book_obj)
return APIResponse(0, 'ok', results=book_ser.data) # 群取
book_obj_list = models.Book.objects.filter(is_delete=False).all()
if not book_obj_list:
return APIResponse(1, '没有数据')
book_ser = serializers.BookV2ModelSerializer(book_obj_list, many=True)
return APIResponse(0, 'ok', results=book_ser.data) # 增加一个
def post(self, request, *args, **kwargs):
book_ser = serializers.BookV2ModelSerializer(data=request.data)
if book_ser.is_valid():
book_obj = book_ser.save()
results = serializers.BookV2ModelSerializer(book_obj).data
return APIResponse(0, 'ok', results=results)
else:
return APIResponse(1, '添加失败', results=book_ser.errors) # 局部修改一个
def patch(self, request, *args, **kwargs):
pk = kwargs.get('pk') if not pk:
return APIResponse(1, 'pk有误') try:
book_obj = models.Book.objects.get(is_delete=False, pk=pk)
except:
return APIResponse(1, 'pk不存在') book_ser = serializers.BookV2ModelSerializer(instance=book_obj, data=request.data, partial=True)
if book_ser.is_valid():
book_obj = book_ser.save()
results = serializers.BookV2ModelSerializer(book_obj)
return APIResponse(0, 'ok', results=results)
else:
return APIResponse(1, '修改失败', results=book_ser.errors) # 整体修改一个 def put(self, request, *args, **kwargs):
pk = kwargs.get('pk')
if not pk:
return APIResponse(1, 'pk有误') try:
book_obj = models.Book.objects.get(is_delete=False, pk=pk)
except:
return APIResponse(1, 'pk不存在') book_ser = serializers.BookV2ModelSerializer(instance=book_obj, data=request.data)
if book_ser.is_valid():
book_obj = book_ser.save()
results = serializers.BookV2ModelSerializer(book_obj).data
return APIResponse(0, 'ok', results=results)
else:
return APIResponse(1, '修改失败', results=book_ser.errors) # 删除
def delete(self, request, *args, **kwargs):
# 单删 /books/(pk)/
# 群删 /books/ 数据包携带 pks => request.data
pk = kwargs.get('pk')
if pk:
pks = [pk, ]
else:
pks = request.data.get('pk') if not pks:
return APIResponse(1, 'pk有误')
book_obj_list = models.Book.objects.filter(is_delete=False, pk__in=pks)
if not book_obj_list.update(is_delete=True):
return APIResponse(1, '删除失败')
return APIResponse(0, '删除成功')

群增群改

因为在ModelSerializer中默认是不能一次性反序列化多个对象,只能反序列化一个对象,所以就不能实现群改和群增功能,所以我们需要在Meta中设置list_serializer_class:

  1. 首先需要自定义ListSerializer字类,可以反序列化多个对象,重写updat方法,将这个类绑定给list_serializer_class
  2. 重新update方法中通过instance接收要更新的对象们,而validated_data绑定需要更新的数据,然后把更新后的instance返回

注意:需要更新的对象和要更新的数据要一一对应

ListSerializer api/serializers.py

from rest_framework import serializers
from . import models class BookListSerializer(serializers.ListSerializer):
def update(self, instance, validated_data):
for ind, obj in enumerate(instance):
for attr, value in validated_data[ind].items():
if hasattr(obj, attr):
set(obj, attr, value)
obj.save()
return instance class BookV2ModelSerializer(serializers.ModelSerializer):
class Meta:
model = models.Book
fields = ('name', 're_name', 'price', 'img', 'publish', 'publish_info', 'authors', 'authors_info')
list_serializer_class = BookListSerializer
extra_kwargs = {
'name':{
'min_length': 3,
'error_messages': {
'min_length': '太短了',
'required': '不能为空'
}
},
'publish':{
'required': True,
'write_only': True,
'error_messages':{
'required': '不能为空'
}
},
'authors':{
'required': True,
'write_only': True,
'error_messages':{
'required': '不能为空'
}
},
} def validate_name(self, value):
if 'sb' in value:
raise serializers.ValidationError('书名包含敏感词汇')
return value def validate(self, attr):
name = attr.get('name')
publish = attr.get('publish')
if models.Book.objects.filter(name=name, publish=publish):
raise serializers.ValidationError(
{'book': '书籍已存在'}
)
return attr

视图层 api/views.py

class BookV2APIView(APIView):
# 群增
def post(self, request, *args, **kwargs):
# 把单增也转换为群增
request_data = request.data
if isinstance(request_data, dict):
data = [request_data, ]
elif isinstance(request, list):
data = request_data
else:
return APIView(1, '数据格式有误') book_ser = serializers.BookV2ModelSerializer(data=data, many=True)
if book_ser.is_valid():
book_obj_list = book_ser.save()
results = serializers.BookV2ModelSerializer(book_obj_list, many=True).data
return APIResponse(0, 'ok', results=results)
else:
return APIResponse(1, '添加失败', results=book_ser.errors) # 群整体改
def put(self, request, *args, **kwargs):
pk = kwargs.get('pk')
request_data = request.data
# 数据的预处理
if pk:
if not isinstance(request_data, dict):
return APIResponse(1, '数据格式有误')
pks = [pk, ]
data = [request_data, ]
elif isinstance(request_data, list):
try:
pks = []
for dic in request_data:
pks.append(dic.pop('pk'))
data = request_data
except:
return APIResponse(1, '数据格式有误')
else:
return APIResponse(1, '数据格式有误') # 将 已删除的书籍 与 不存在的书籍 剔除 (提供修改的数据相对应也剔除)
book_obj_list = []
filter_data = []
for ind, pk in enumerate(pks):
book_obj = models.Book.objects.filter(is_delete=False, pk=pk).first()
if book_obj:
book_obj_list.append(book_obj)
filter_data.append(data[ind]) # 反序列化完成数据的修改
book_ser = serializers.BookV2ModelSerializer(instance=book_obj_list, data=filter_data, many=True)
if book_ser.is_valid():
book_obj_list = book_ser.save()
results = serializers.BookV2ModelSerializer(book_obj_list, ).data
return APIResponse(0, 'ok', results=results)
else:
return APIResponse(1, '修改失败', results=book_ser.errors) # 群部分改
def patch(self, request, *args, **kwargs):
"""
单改 /books/(pk)/ data {"name": "new_name", ...}
群改 /books/ data [{"pk": 1, "name": "new_name", ...}, ...,{"pk": n, "name": "new_name", ...}]
结果:
pks = [1, ..., n] => book_query => instance
data = [{"name": "new_name", ...}, ..., {"name": "new_name", ...}] => data
"""
pk = kwargs.get('pk')
request_data = request.data
# 数据的预处理
if pk:
if not isinstance(request_data, dict):
return APIResponse(1, '数据格式有误')
pks = [pk, ]
data = [request_data, ]
elif isinstance(request_data, list):
try:
pks = []
for dic in request_data:
pks.append(dic.pop('pk'))
data = request_data
except:
return APIResponse(1, '数据格式有误')
else:
return APIResponse(1, '数据格式有误') # 将 已删除的书籍 与 不存在的书籍 剔除 (提供修改的数据相对应也剔除)
book_obj_list = []
filter_data = []
for ind, pk in enumerate(pks):
book_obj = models.Book.objects.filter(is_delete=False, pk=pk).first()
if book_obj:
book_obj_list.append(book_obj)
filter_data.append(data[ind]) # 反序列化完成数据的修改
book_ser = serializers.BookV2ModelSerializer(instance=book_obj_list, data=filter_data, many=True)
# 校验通过保存
if book_ser.is_valid():
book_obj_list = book_ser.save()
results = serializers.BookV2ModelSerializer(book_obj_list, ).data
return APIResponse(0, 'ok', results=results)
# 校验失败返回
else:
return APIResponse(1, '修改失败', results=book_ser.errors)

drf--ModelSerializers序列化的更多相关文章

  1. Django框架深入了解_02(DRF之序列化、反序列化)

    序列化:将Python对象准换成json格式的字符串,反之即为反序列化 DRF的序列化使用过程: 使用drf的序列化组件 -1 新建一个序列化类继承Serializer -2 在类中写要序列化的字段 ...

  2. drf框架序列化和返序列化

    0903自我总结 drf框架序列化和反序列化 from rest_framework import serializers 一.自己对于序列化和反序列化使用的分类 前后端交互主要有get,post,p ...

  3. drf框架 - 序列化组件 | Serializer

    序列化组件 知识点:Serializer(偏底层).ModelSerializer(重点).ListModelSerializer(辅助群改) 序列化与反序列化 序列化: 将对象序列化成字符串用户传输 ...

  4. Django drf:序列化增删改查、局部与全局钩子源码流程、认证源码分析、执行流程

    一.序列化类的增.删.改.查 用drf的序列化组件   -定义一个类继承class BookSerializer(serializers.Serializer):   -写字段,如果不指定source ...

  5. 第三章、drf框架 - 序列化组件 | Serializer

    目录 第三章.drf框架 - 序列化组件 | Serializer 序列化组件 知识点:Serializer(偏底层).ModelSerializer(重点).ListModelSerializer( ...

  6. DRF的序列化组件

    目录 DRF的序列化组件 Serializer组件 序列化 反序列化 ModelSerializer组件 序列化和反序列化 自定义Response方法 基表相关 DRF中ORM的多表关联操作 外键设计 ...

  7. DRF如何序列化外键的字段

    我觉得在有些应用场景下,这个操作是有用的,因为可以减少一个AJAX的请求,以增加性能. 当然,是二次请求,还是一次传输.这即要考虑用户体验,还要兼顾服务器性能. 一切是有条件的平衡吧.就算是一次传输, ...

  8. DRF中序列化器定义及使用

    首先需要明白序列化和反序列化的定义及作用: 序列化是将程序语言转换为JSON/XML; 反序列化是将JSON/XML转换为程序语言; 对应到Django中,序列化即把模型对象转换为字典形式, 在返回给 ...

  9. drf之序列化器的使用

    一.序列化器-Serializer 作用: 1. 序列化,序列化器会把模型对象转换成字典,经过response以后变成json字符串 2. 完成数据校验功能 3. 反序列化,把客户端发送过来的数据,经 ...

  10. Django框架(十八)—— drf:序列化组件(serializer)

    序列化组件 # 模型层 from django.db import models class Book(models.Model): nid = models.AutoField(primary_ke ...

随机推荐

  1. isaster(Comet OJ - Contest #11D题+kruskal重构树+线段树+倍增)

    目录 题目链接 思路 代码 题目链接 传送门 思路 \(kruskal\)重构树\(+\)线段树\(+\)倍增 代码 #include <set> #include <map> ...

  2. Kubernetes Pod的数据卷Volume

    概述 由于容器本身是非持久化的,因此需要解决在容器中运行应用程序遇到的一些问题.首先,当容器崩溃时,kubelet将重新启动容器,但是写入容器的文件将会丢失,容器将会以镜像的初始状态重新开始:第二,在 ...

  3. Pycharm过期了怎么办?

    Pycharn过期了,打开Pycharm,选择help,倒数第三个register,用激活码进行激活,具体情况见激活码,侵删

  4. FormData用法

    https://www.jianshu.com/p/230e7000cefd FormData的主要用途有两个: 1.将form表单元素的name与value进行组合,实现表单数据的序列化,从而减少表 ...

  5. RabbitMQ消息幂等性问题

    文章目录 1. 什么是幂等性?1.1 消息队列的幂等性1.2 模拟重试机制1.2.1 生产者代码1.2.2 消费者代码1.2.3 消费者 application.yml 配置2. 如何保证消息幂等性, ...

  6. 洛谷 P2615 神奇的幻方

    传送门 I'm here! 思路 这个题,我们可以直接去模拟,因为范围很小,且\(N\)都是奇数 直接构造一个矩阵,初始值都为\(0\),然后\(while\)循环,根据题目给出的\(4\)个条件进行 ...

  7. 【JZOJ5740】【20190706】幻想世界

    题目 小 $\omega $ 想要进行烟火表演,她一开始有\(n\)颗彗星和\(n\)颗陨石 如果小 \(\omega\) 有\(i\)颗彗星而没有陨石,那么她会消耗\(i\)颗彗星并得到\(a_i\ ...

  8. flex布局整理以及demo

    flex: 概念: flex容器: display:flex flex项: 轴:水平的主轴.垂直的交叉轴 容器的属性: 1. flex-direction(决定主轴的方向.即项目的排列方向) 1.1 ...

  9. Linux上使用yum安装openjdk

    来源:https://www.cnblogs.com/yaun1498078591/p/10368884.html 使用yum查找jdk: yum search java|grep jdk # yum ...

  10. 解决github打不开

    今天重庆电信的“临时工”把github废了. 主要是github.githubassets.com和customer-stories-feed.github.com访问不到 通过修改host的方式上g ...