一、序列化类
1、介绍
序列化组件在 DRF 中扮演着重要的角色,帮助开发者轻松地定义数据的序列化和反序列化过程,同时提供了数据验证、字段定义、嵌套序列化等功能。通过使用序列化组件,您可以更好地控制 API 的数据输入和输出,确保数据的有效性和一致性。
serializers.Serializer
是基本的序列化组件,用于定义如何将数据转换为可传输的格式以及如何反序列化数据。开发者可以通过定义字段和序列化逻辑来定制 Serializer 类。
2、作用
(1)字段定义
- 序列化类允许开发者定义各种字段,包括模型字段、自定义字段和计算字段等,以控制数据的序列化和反序列化过程。
(2)数据验证
- 序列化类提供了数据验证功能,可以确保客户端提交的数据符合预期的格式和规则,保证数据的有效性和一致性。
(3)嵌套序列化
- 序列化类支持嵌套序列化,允许处理复杂的数据结构和关联关系,使得在 API 中展示相关数据变得更加灵活。
(4)自定义序列化逻辑
- 开发者可以在序列化类中实现自定义的序列化逻辑,包括数据转换、计算字段值、条件逻辑等,以满足特定的业务需求。
3、主要方法
(1)to_representation(self, instance)
- 该方法用于将模型实例转换为序列化后的数据表示形式。在这个方法中,开发者可以定义如何将模型数据转换为序列化后的格式。
(2)to_internal_value(self, data)
- 该方法用于将客户端提交的数据转换为内部数值表示形式,通常用于反序列化过程中。在这个方法中,数据通常会被验证和转换为适合存储或处理的形式。
(3)create(self, validated_data)
- 如果序列化类用于创建新的模型实例,开发者可以实现该方法来定义如何创建新的实例。在该方法中,通常会使用传入的验证过的数据来创建新的对象。
(4)update(self, instance, validated_data)
- 如果序列化类用于更新现有的模型实例,开发者可以实现该方法来定义如何更新现有的实例。在该方法中,通常会使用传入的验证过的数据来更新现有对象。
(5)validate_<field_name>(self, value)
- 对于每个字段,开发者可以定义一个以
validate_<field_name>
命名的方法,用于对特定字段进行额外的验证。这些方法通常用于执行字段级别的验证逻辑。
4、小结
- 序列化
- 序列化器会把模型对象(queryset,单个对象)转换成字典
- 经过response以后变成json字符串
- 反序列化
- 把客户端发送过来的数据,经过request.data以后变成字典
- 序列化器可以把字典转成模型
- 反序列化
- 可以在反序列化保存到数据库之前,做数据库校验
二、序列化应用场景
1、使用步骤
- 写一个序列化的类,并继承Serializer
- 在类中写要序列化的字段, 序列化字段类(有很多, 常用的就几个, 等同于models中的字段类)
- 在视图类中使用, 导入序列化类把要序列化的对象传入, 得到序列化对象
- 可以通过
[序列化类的对象].data
获取序列化后的字典或者列表(不是json格式字符串),然后交给Response返回json格式的字符串
2、创建模型表
- models.py
from django.db import models
class Book(models.Model):
name = models.CharField(max_length=32)
price = models.IntegerField()
publish = models.CharField(max_length=32)
3、创建序列化文件
- serializer.py
from rest_framework import serializers
class BookSerializer(serializers.Serializer):
name = serializers.CharField()
price = serializers.IntegerField()
publish = serializers.CharField()
4、 视图函数中使用序列化
- view.py
from .serializer import BookSerializer
from rest_framework.response import Response
class BookView(APIView):
def get(self, request):
obj_list = Book.objects.all()
# 要序列化的Queryset对象,但是如果是多条数据,必须加 many=True
serializer=BookSerializer(instance=obj_list,many=True) # instance=None, data=empty
return Response({'code': 100, 'msg': '查询成功', 'results':serializer.data})
class BookDetailView(APIView):
def get(self, request, pk):
obj = Book.objects.filter(pk=pk).first()
# 单条,不用传many=True,非要传many=Fasle
serializer=BookSerializer(instance=obj)
return Response({'code':100,'msg':'成功','result':serializer.data})
4、路由
- urls.py
from django.urls import path
from app01.views import BookView, BookDetailView
urlpatterns = [
path('books/', BookView.as_view()),
path('books/<int:pk>', BookDetailView.as_view()),
]
5、测试
- 序列化类中写了三个序列化字段,展示了三个序列化的字段
- 注释掉publish之后,发现只剩两个字段了
三、常用序列化字段和参数
1、常用字段
字段 | 字段构造方式 | 详解 |
---|---|---|
BooleanField | BooleanField() | 布尔字段用于存储和表示真/假值。构造方法不需要参数。 |
NullBooleanField | NullBooleanField() | 可空布尔字段是可以接受三个值的布尔字段:True、False和None(空值)。构造方法不需要参数。 |
CharField | CharField(max_length=None, min_length=None, allow_blank=False, trim_whitespace=True) | 字符字段用于存储短文本数据。max_length指定字符的最大长度,min_length指定最小长度。allow_blank指定是否允许为空值。trim_whitespace指定是否在保存数据前去除首尾的空格。 |
EmailField | EmailField(max_length=None, min_length=None, allow_blank=False) | Email字段用于存储和验证电子邮件地址。max_length指定最大字符长度,min_length指定最小长度。allow_blank指定是否允许为空值。 |
RegexField | RegexField(regex, max_length=None, min_length=None, allow_blank=False) | 正则表达式字段用于存储和验证符合特定模式的数据。regex指定正则表达式,max_length指定最大字符长度,min_length指定最小长度。allow_blank指定是否允许为空值。 |
SlugField | SlugField(max_length=50, min_length=None, allow_blank=False) 正则字段,验证正则模式 [a-zA-Z0-9_-]+ | Slug字段用于存储URL友好的文本标识符。max_length指定最大字符长度,min_length指定最小长度。allow_blank指定是否允许为空值。 |
URLField | URLField(max_length=200, min_length=None, allow_blank=False) | URL字段用于存储和验证URL地址。max_length指定最大字符长度,min_length指定最小长度。allow_blank指定是否允许为空值。 |
UUIDField | UUIDField(format=‘hex_verbose’) format: 1) 'hex_verbose' 如"5ce0e9a5-5ffa-654b-cee0-1238041fb31a" 2) 'hex' 如 "5ce0e9a55ffa654bcee01238041fb31a" 3)'int' - 如: "1234567012312313134124512351145114" 4)'urn' 如: "urn:uuid:5ce0e9a5-5ffa-654b-cee0-1238041fb31a" | UUID字段用于存储和验证通用唯一标识符。format参数指定UUID的格式。 |
IPAddressField | IPAddressField(protocol=‘both’, unpack_ipv4=False, **options) | IP地址字段用于存储和验证IP地址。protocol参数指定所允许的IP地址协议类型,unpack_ipv4参数指定是否拆分IPv4地址。 |
IntegerField | IntegerField(max_value=None, min_value=None) | 整数字段用于存储整数值。max_value指定最大值,min_value指定最小值。 |
FloatField | FloatField(max_value=None, min_value=None) | 浮点数字段用于存储浮点数值。max_value指定最大值,min_value指定最小值。 |
DecimalField | DecimalField(max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None) max_digits: 最多位数 decimal_palces: 小数点位置 | 十进制字段用于存储精确的十进制数值。max_digits指定最多位数,decimal_places指定小数点位置。coerce_to_string指定是否将值强制转化为字符串形式。max_value指定最大值,min_value指定最小值。 |
DateTimeField | DateTimeField(format=api_settings.DATETIME_FORMAT, input_formats=None) | 日期时间字段用于存储日期和时间。format参数指定日期时间的输出格式,input_formats参数指定输入格式。 |
DateField | DateField(format=api_settings.DATE_FORMAT, input_formats=None) | 日期字段用于存储日期。format参数指定日期的输出格式,input_formats参数指定输入格式。 |
TimeField | TimeField(format=api_settings.TIME_FORMAT, input_formats=None) | 时间字段用于存储时间。format参数指定时间的输出格式,input_formats参数指定输入格式。 |
DurationField | DurationField() | 持续时间字段用于存储一段时间的持续时间。构造方法不需要参数。 |
ChoiceField | ChoiceField(choices) choices与Django的用法相同 | 选择字段用于存储和验证预定义选项中的一个值。choices参数指定可选的选项值。 |
MultipleChoiceField | MultipleChoiceField(choices) | 多选字段用于存储和验证多个预定义选项中的值。choices参数指定可选的选项值。 |
FileField | FileField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL) | 文件字段用于上传和保存文件。max_length指定文件名的最大长度,allow_empty_file指定是否允许为空文件。use_url指定是否使用文件的URL路径。 |
ImageField | ImageField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL) | 图片字段用于上传和保存图片文件。max_length指定文件名的最大长度,allow_empty_file指定是否允许为空文件。use_url指定是否使用图片的URL路径。 |
ListField | ListField(child=, min_length=None, max_length=None) | 列表字段用于存储和验证列表类型的数据。child参数指定列表中元素的类型,min_length指定最小长度,max_length指定最大长度。 |
DictField | DictField(child=) | 字典字段用于存储和验证字典类型的数据。child参数指定字典中value的类型。 |
- 总结:常用字段
IntegerField
CharField
DateTimeField
DecimalField
ListField和DictField
2、字段参数(校验数据)
(1)选项参数:(CharField,IntegerField)
参数名称 | 作用 |
---|---|
max_length | 最大长度 |
min_lenght | 最小长度 |
allow_blank | 是否允许为空 |
trim_whitespace | 是否截断空白字符 |
max_value | 最小值 |
min_value | 最大值 |
(2)通用参数:
参数名称 | 说明 |
---|---|
read_only | 表明该字段仅用于序列化输出,默认False |
write_only | 表明该字段仅用于反序列化输入,默认False |
required | 表明该字段在反序列化时必须输入,默认True |
default | 反序列化时使用的默认值 |
allow_null | 表明该字段是否允许传入None,默认False |
- 非常重要的参数
read_only
write_only
3、案例展示
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
def validate_name(name):
if name.startswith('sb'):
raise ValidationError('不能以sb开头')
else:
return name
class TaskSerializer(serializers.Serializer):
task_name = serializers.CharField(required=False,allow_null=True,default='默认任务')
task_id = serializers.CharField(max_length=8,error_messages={'max_length':'太长了,受不了了','required':'不能没有你'})
task_time = serializers.DateTimeField()
四、反序列化应用场景
-
使用序列化器进行反序列化时,需要对数据进行验证后,才能获取验证成功的数据或保存成模型类对象。验证的规则在定义序列化类时已经定义
-
在获取反序列化的数据前,必须调用 is_valid() 方法进行验证,验证成功返回 True,否则返回 False
-
验证失败,可以通过序列化器对象的 errors 属性获取错误信息,返回字典,包含了字段和字段的错误
-
验证成功,可以通过序列化器对象的 validated_data 属性获取数据。
1、校验数据(增加)
(1)自定义序列化文件
- serializer.py
- 校验前端传入的数据
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
class BookSerializer(serializers.Serializer):
# 1. 数据校验第一层,字段自己校验
# 名字最长8位,最短3位
name = serializers.CharField(max_length=8, min_length=3) # 字段参数,控制 数据校验
price = serializers.IntegerField(max_value=100, min_value=10)
publish = serializers.CharField()
# 2. 局部钩子函数--->给某个字段加限制条件
def validate_name(self, name):
# 书名不能以66开头
# if name.startswith('66'):
# 书名不能有66
if '66' in name:
# 不合法,抛出异常
raise ValidationError('书名不能以66开头')
else:
# 合法,返回原来的数据
return name
"""
局部钩子,一个字段只能写一个
全局钩子,也只能写一个
"""
# 3 全局钩子--->多个字段校验
# 要求:书名和出版社不能一样
def validate(self, attrs):
# attrs 是前端传入,经过字段自己校验和局部钩子校验都通过的数据 [字典]
if attrs.get('name') == attrs.get('publish'):
raise ValidationError('书名和出版社不能一样')
else:
return attrs
(2)视图函数
- views.py
class BookView(APIView):
def post(self, request):
# 1 校验前端传入的数据
# 2 数据校验和反序列化---> 不能传instance,要传data
serializer=BookSerializer(data=request.data)
# 3 校验数据
if serializer.is_valid():
# 校验通过,保存
serializer.save() # 需要在序列化类中重写 create才能存进去
return JsonResponse({'code': 100, 'msg': '保存成功'})
else:
return JsonResponse({'code': 101, 'msg': serializer.errors})
(3)小结
反序列化有三层校验:
- 字段自己的:写的字段参数:required/max_length …
- 局部钩子:写在序列化类中的方法
- 方法名必须是 validate_字段名
- 全局钩子:写在序列化类中的方法
- 方法名必须是 validate
全局钩子和局部钩子都只能写一个,优先级是全局 > 局部
2、保存数据(增加)
- 上面数据校验完成最后有个数据保存
(1)自定义序列化文件
- serializer.py
# 数据保存
def create(self, validated_data):
# validated_data: 前端传入,所有校验通过的数据 字典
book = Book.objects.create(**validated_data)
return book # 不要忘了返回
(2)视图函数
- views.py
class BookView(APIView):
def post(self, request):
# 1 校验前端传入的数据
# 2 数据校验和反序列化---》不能传instance,要传data
serializer=BookSerializer(data=request.data)
# 3 校验数据
if serializer.is_valid():
# 校验通过,保存
serializer.save() # 需要在序列化类中重写 create才能存进去
return JsonResponse({'code': 100, 'msg': '保存成功'})
else:
return JsonResponse({'code': 101, 'msg': serializer.errors})
(3)小结
- 在后端逻辑中调用 save 方法,然后序列化类中需要重写 create 方法
- 但是如果调用create方法,校验通过的 validated_data 数据不会自动传入,所以需要我们传入的数据
serializer = BookSerializer(data=request.data)
3、反序列化之修改并保存
(1)自定义序列化文件
- serializer.py
def update(self, instance, validated_data):
# instance:对象
# validated_data 数据
for i,v in validated_data.items():
setattr(instance, i, v)
instance.save()
return instance
(2)视图函数
- views.py
# 127.0.0.1:8000/app01/books/1
class BookDetailView(APIView):
def put(self, request, pk):
obj=Book.objects.filter(pk=pk).first()
# 改对象必须传data和instance
serializer=BookSerializer(instance=obj,data=request.data)
if serializer.is_valid():
serializer.save() # 重写update 触发序列化类的update
return Response({'code':100,'msg':'成功'})
else:
return Response({'code': 100, 'msg': serializer.errors})
(3)小结
- 视图函数传入的instance不一定是对象,也可以传id到序列化类来使用
- 序列化类中重写 update 方法,在后端逻辑中调用 save 方法
- 需要传入的参数
ser = BookSerializer(instance=book,data=request.data)
五、save方法加分析
- 在视图类中,无论是保存还是修改,都是调用序列化类的 save() 方法
- 底层实现逻辑是根据 instance 判断需要做出的对应的方法
def save(self, **kwargs):
# 根据传入的 instance 对象,进行方式的判断 update/create
# 如果是 update 方法 需要传入需要需要修改的独享和修改的数据
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:
# 如果是 create 需要传入修改的数据
self.instance = self.create(validated_data)
assert self.instance is not None, (
'`create()` did not return an object instance.'
)
return self.instance
- 如果是 update 方法 需要传入需要需要修改的独享和修改的数据
- 如果是 create 需要传入修改的数据
六、read_only 和 write_only详细介绍
1、常用字段参数
- read_only:表明该字段仅用于序列化输出,默认False(只能查看,不能修改)
- write_only:表明该字段仅用于反序列化输入,默认False(只能修改,不能查看)
2、案例展示
(1)路由
- urls.py
path('books/<int:pk>', BookDetailView.as_view()),
(2)模型表
- models.py
from django.db import models
class Book(models.Model):
name = models.CharField(max_length=64)
price = models.IntegerField()
publish = models.CharField(max_length=32)
(3)序列化类
- serializers.py
from rest_framework import serializers
class BookSerializer2(serializers.Serializer):
name = serializers.CharField(max_length=8, min_length=3)
# read_only=True:该字段只用于序列化输出,只能看不能修改
price = serializers.IntegerField(max_value=100, min_value=10,read_only=True)
# write_only=True:该字段只用于反序列化输入,只能改不能查看
publish = serializers.CharField(write_only=True)
def update(self, instance, validated_data):
for k,v in validated_data.items():
setattr(instance, k, v)
instance.save()
return instance
(4)视图函数
- views.py
from mydrf.serializers import BookSerializer2
from mydrf import models
from rest_framework.views import APIView
from rest_framework.response import Response
class BookDetailView(APIView):
def get(self, request,pk):
# 从数据库中获取到Book的对象
obj = Book.objects.filter(pk=pk).first()
# 将book_obj放进我们写的序列化器里面进行序列化
book_ser = BookSerializer(obj)
# [序列化对象].data就是序列化后得到的字典
return Response(book_ser.data)
def put(self, request, pk):
obj = Book.objects.filter(pk=pk).first()
# 改对象必须传data和instance
serializer = BookSerializer(instance=obj, data=request.data)
if serializer.is_valid():
serializer.save() # 重写update 触发序列化类的update
return Response({'code': 100, 'msg': '成功'})
else:
return Response({'code': 100, 'msg': serializer.errors})
(5)测试
- 首先发送get请求从数据库中获取id为1的数据
- 发现获取不到publish的值,因为我们设置了write_only
- 然后发送put请求携带参数在数据库中修改id为1的数据
- 修改成功了,但是数据库中price值并没有发生改变
- 最后对publish进行测试
- 在发送put请求的时候我并没有携带publish的键值对数据
- 于是返回结果为这个字段是必填项的提示信息
七、补充:翻译函数
在 Django REST framework (DRF) 中,翻译函数通常用于处理序列化类中字段的翻译工作。DRF 提供了 django.utils.translation.ugettext_lazy
函数来实现字段值的翻译。这个函数用于延迟翻译字符串,以便在渲染时执行实际的翻译。
下面是一个简单的示例,演示如何在 DRF 的序列化类中使用翻译函数:
from django.utils.translation import ugettext_lazy as _
class MySerializer(serializers.Serializer):
name = serializers.CharField(label=_("Name"))
age = serializers.IntegerField(label=_("Age"))
# 在视图中使用这个序列化类
serializer = MySerializer(data=data)
serializer.is_valid()
在这个示例中,_()
函数被用来标记需要翻译的字段标签。当这个序列化类被实例化时,标签会被翻译成当前语言环境下的字符串。
另外一定要确保在项目的设置中配置了正确的语言和翻译设置,以便让翻译函数生效。
八、序列化类实现增删改查接口
1、需求
- 数据库表 : Book, Author, AuthorDatail, Publish, 表之间建好关联
- 实现出版社查全部, 查某条, 新增, 修改, 删除接口
- 出版社名字不能有"sb"敏感字
- 书名和出版社不能一样
2、代码实现
(1)创建表模型
- models.py
from django.db import models
class Book(models.Model):
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
price = models.DecimalField(max_digits=5, decimal_places=2)
publish_date = models.DateField()
publish = models.ForeignKey(to='Publish', to_field='nid', on_delete=models.CASCADE)
authors = models.ManyToManyField(to='Author')
def __str__(self):
return self.name
class Author(models.Model):
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
age = models.IntegerField()
author_detail = models.OneToOneField(to='AuthorDetail', to_field='nid', unique=True, on_delete=models.CASCADE)
class AuthorDetail(models.Model):
nid = models.AutoField(primary_key=True)
telephone = models.BigIntegerField()
birthday = models.DateField()
addr = models.CharField(max_length=64)
class Publish(models.Model):
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
city = models.CharField(max_length=32, null=True)
email = models.EmailField()
def __str__(self):
return self.name
(2)序列化类
- serializers.py
class BookSerializer(serializers.Serializer):
# nid = serializers.IntegerField(required=False)
book_id = serializers.IntegerField(source='nid', required=False)
name = serializers.CharField(max_length=32)
# book_name = serializers.CharField(source='name')
price = serializers.DecimalField(max_digits=5, decimal_places=2)
publish_date = serializers.DateField()
# publish_id = serializers.IntegerField()
publish_name = serializers.CharField(source='publish.name')
def validate_name(self, name):
if 'sb' in name:
# 不合法,抛异常
raise ValidationError('书名不能有sb')
else:
# 合法,返回原来的数据
return name
# 局部钩子,一个字段只能写一个
# 全局钩子,也只能写一个
# 3 全局钩子 多个字段校验
def validate(self, attrs):
# attrs 是前端传入,经过字段自己校验和局部钩子校验都通过的数据 [字典]
name = attrs.get('name')
price = attrs.get('price')
if name == '海底两万里' and price == 100:
raise ValidationError('该书已被列入黑名单')
return attrs
def create(self, validated_data):
res = models.Book.objects.create(**validated_data)
return res # 不要忘了返回
def update(self, instance, validated_data):
for k, v in validated_data.items():
setattr(instance, k, v)
instance.save()
return instance
(3)视图函数
- views.py
class BookView(APIView):
def get(self, request):
book_qs = models.Book.objects.all()
book_str = BookSerializer(instance=book_qs, many=True) # 多条数据一定要添加many
return Response(book_str.data)
def post(self, request):
book_ser = BookSerializer(data=request.data)
if book_ser.is_valid():
book_ser.save()
return Response(book_ser.data)
else:
return Response(book_ser.errors)
class BookDetailView(APIView):
def get(self, request, pk):
book_obj = models.Book.objects.filter(pk=pk).first()
book_ser = PublishSerializer(instance=book_obj)
return Response(book_ser.data)
def put(self, request, pk):
book_obj = models.Book.objects.filter(pk=pk).first()
book_ser = BookSerializer(instance=book_obj, data=request.data)
if book_ser.is_valid():
book_ser.save()
return Response(book_ser.data)
else:
return Response(book_ser.errors)
def delete(self, request, pk):
# 直接删除数据,拿到影响条数的一个列表
rows = models.Book.objects.filter(pk=pk).delete()
if rows[0] > 0:
return Response('数据删除成功!')
else:
return Response('数据不存在!')
(4)路由
- urls.py
path('books/', BookView.as_view()),
path('books/<int:pk>/', BookDetailView.as_view())
九、多表关联序列化
1、定制返回格式之source
用来指定要序列化的字段(数据表中的字段), 一般使用在一个字段情况, 可以跨表, 可以执行方法并执行
- 不指定,默认就是字段名,必须跟数据库对应
- 指定了source就可以给字段改名了
# 更改字段名
"[另取的名字]" = serializers.CharField(source='nid') # 指定需要序列化的字段是publish
# 执行方法,指定一个定义在模型类内方法的内存地址,会自动加括号执行
publish_date = serializers.CharField(source='方法名')
- 如果想跨表查询的话,本表中一定要有所要跨的表的字段
比如下面的publish,从BOOK表我们就可以跨表查询到Publish表中的字段
# 1 source 定制返回字段名,跟name必须对应
book_name = serializers.CharField(source='name')
# 2 显示出版社名字--> 字符串类型--> CharField 可以跨表查询
publish_name=serializers.CharField(source='publish.name')
# 3 所有字段都可以被转成CharField
publish_id=serializers.IntegerField(source='publish.id')
2、定制返回字段
我想实现如下格式返回的数据该怎么操作
{name:书名,price:价格,publish:{name:出版社名,addr:地址},authors:[{},{}]}
(1)在表模型中定义方法
- models.py
class Book(models.Model):
name = models.CharField(max_length=32)
price = models.DecimalField(max_digits=5, decimal_places=2)
publish_date = models.DateField()
publish = models.ForeignKey(to='Publish',on_delete=models.CASCADE)
authors=models.ManyToManyField(to='Author') # 数据库中不会有这个字段---》生成第三张表--》多对多
def book_name(self):
# 通过这种方式,可以修改返回的格式
return self.name+'sb'
@property
def publish_detail(self):
return {'name':self.publish.name,'city':self.publish.city}
@property
def authors_list(self):
l=[]
for author in self.authors.all():
l.append({'name':author.name,'age':author.age})
return l
def __str__(self):
return self.name
- serializer.py
class BookSerializer(serializers.Serializer):
name = serializers.CharField()
price = serializers.DecimalField(max_digits=5, decimal_places=2)
publish_date = serializers.DateField()
# 出版社对象
# publish = serializers.CharField() # 如果这样写,会把publish对象,打印的样子返回给前端
publish_detail = serializers.DictField() # 表里有这个字段吗? 有
# 所有作者
authors_list = serializers.ListField() # 表里没有这个字段,表模型中有
(2)通过SerializerMethodField
- 一定要配合一个方法–>
get_字段名
- serializer.py
class BookSerializer(serializers.Serializer):
book_name = serializers.SerializerMethodField()
def get_book_name(self,obj):
return obj.name+'ssssb'
price = serializers.DecimalField(max_digits=5, decimal_places=2)
publish_date = serializers.DateField()
# 一定要配合一个方法--> get_字段名
publish_detail = serializers.SerializerMethodField()
def get_publish_detail(self,obj):
# obj 就是当前序列化到的book对象
return {'name':obj.publish.name,'city':obj.publish.city,'id':obj.pk}
authors_list = serializers.SerializerMethodField()
def get_authors_list(self,obj):
l=[]
for author in obj.authors.all():
l.append({'name':author.name,'age':author.age,'id':author.id})
return l
(3)子序列化
- 需要创建一个子序列化类
- 子序列化类必须写在上方, 且只能对外键字段进行重写
- 使用了子序列化的外键字段就不能再进行反序列化过程
- 子序列化单个数据需填写
many=False
, 多个数据需填写many=True
# serializers.py 文件中创建子序列化类,并将子序列化类加入序列化类中
# 子序列化类 必须写在上方
class PublishSerializer(serializers.Serializer):
id = serializers.IntegerField()
name = serializers.CharField()
city = serializers.CharField()
email = serializers.EmailField()
# 子序列化类 必须写在上方
class AuthorSerializer(serializers.Serializer):
id = serializers.IntegerField()
name = serializers.CharField()
age = serializers.CharField()
class BookSerializer(serializers.Serializer):
name = serializers.CharField()
price = serializers.DecimalField(max_digits=5, decimal_places=2)
publish_date = serializers.DateField()
# 书写子序列化字段
publish_detail = PublishSerializer(source='publish')
# 多条 many=True
authors_list = AuthorSerializer(source='authors',many=True)
十、多表关联反序列化
- 序列化的字段和反序列化字段不一致
- 1 笨办法:再写个序列化类,单独用来反序列化
- 2 通过 read_only write_only 控制
1、反序列化保存一对多关联字段
publish
字段一对多关系绑定了Publish
表,因此在Book
表中显示的是publish_id
- 在反序列化校验Book对象时,django并不认识
Publish
表,因此只会识别publish_id
,那么前端发送请求时也要传入publish_id
# serializers.py
class BookSerializer(serializers.Serializer):
name = serializers.CharField(max_length=32)
price = serializers.DecimalField(max_digits=5, decimal_places=2)
publish_date = serializers.DateField()
# 这个字段用来做序列化
publish_detail = PublishSerializer(source='publish', read_only=True)
# 反序列化字段--> 只用来保存---> 多表关联
publish_id = serializers.IntegerField(write_only=True) # 前端传入数字---》自动跟出版社id做对应
在反序列化校验publish_id时为了不影响原先的序列化校验,要在不需要反序列化校验的参数后面加入read_only=True参数,同时publish_id加入write_only参数只作用于反序列化校验
read_only=True:该字段只会用于序列化校验
write_only=True:该字段只会用于反序列化校验
(1)重写create方法
def create(self, validated_data):
publish_id = validated_data.pop('publish_id')
book_obj = models.Book.objects.create(**validated_data, publish_id=publish_id)
return book_obj
- 为什么要先pop掉publish_id呢
- 如果这是我直接
book_obj = models.Book.objects.create(**validated_data)
会发生什么? print(book_obj.publish_id) ---> None
这是因为,虽然validated_data
包含了publish_id
字段,但是此时它并不具备外键属性,Django仅将他作为一个普通字段进行反序列化,那么理所当然的后端的序列化结果也不会成功,因为publish_detail
是根据外键属性取值的,所以此时这里应该明确用publish_id
作为publish_id
字段的参数
# 先将publish_id参数从vaildated_data中踢出,让前两个参数以**方式传入后再指定publish_id
publish_id = validated_data.pop('publish_id')
# 左边的publish_id是字段,右边的publish_id是前端传来的数据
book_obj = models.Book.objects.create(**validated_data, publish_id=publish_id)
# 这种方法和上面的 **validated_data 含义相同,用其中一种即可
book_obj = models.Book.objects.create(name=validated_data.get('name'), price=validated_data.get('price'), publish_date=validated_data.get('publish_date'), publish_id=publish_id)
(2)重写upadate方法
- views.py
def put(self, request, pk):
book_obj = models.Book.objects.filter(pk=pk).first()
book_ser = BookSerializer(instance=book_obj, data=request.data)
if book_ser.is_valid():
book_ser.save()
return Response(book_ser.data)
else:
return Response(book_ser.errors)
- serializers.py
def update(self, instance, validated_data):
publish_id = validated_data.pop('publish_id')
book_qs = Book.objects.filter(pk=instance.pk)
book_qs.update(**validated_data, publish_id=publish_id)
instance = book_qs.first()
return instance
- updata相比于create就多了个instance参数
instance
就是views层传过来的模型对象,先pop掉publish_id
,然后直接update即可
2、反序列化保存多对多关联字段
- 首先定义一个
author
字段,与Author表绑定多对多关系字段
class Book(models.Model):
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
price = models.DecimalField(max_digits=5, decimal_places=2)
publish_date = models.DateField(auto_now=True)
authors = models.ManyToManyField(to='Author')
class Author(models.Model):
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
age = models.IntegerField()
那么此时的数据便不会存在于Book
或Author
中,而是自动新建一个book_author
表,例如:
其次在序列化类中添加字段
class BookSerializer(serializers.Serializer):
name = serializers.CharField()
price = serializers.DecimalField(max_digits=5, decimal_places=2)
publish_date = serializers.DateField(required=False,default=datetime.now)
# 这个字段用来做序列化
publish_detail = PublishSerializer(source='publish',read_only=True)
# 这个字段用来做序列化
authors_list = AuthorSerializer(source='authors', many=True,read_only=True)
# 反序列化字段--> 只用来保存---> 多表关联
publish=serializers.IntegerField(write_only=True) # 前端传入数字---> 自动跟出版社id做对应
authors=serializers.ListField(write_only=True) # ListFieled字段
# 反序列化字段-->可以随意命名,跟表字段没关系--》但是后续保存和修改要对应好才行
# publish_id=serializers.IntegerField(write_only=True) # 前端传入数字---> 自动跟出版社id做对应
# authors_xx=serializers.ListField(write_only=True)
(1)重写create方法
- serializers.py
def create(self, validated_data):
authors=validated_data.pop('authors')
book=Book.objects.create(**validated_data)
book.authors.add(*authors) # 向中间表中插入数据
return book
- 首先将
authors
踢出,然后将其单独插入中间表,具体原因跟一对多关系相同
(2)重写update方法
- serializers.py
def update(self, instance, validated_data):
authors=validated_data.pop('authors')
book_qs=Book.objects.filter(pk=instance.pk) # 查询qs对象
book_qs.update(**validated_data) # 使用qs更新
instance=book_qs.first() # 单个对象
instance.authors.set(authors) # 向中间表中插入数据
# instance.author.clear()
# instance.authors.add(*authors)
return instance
知识回顾:模型层多表操作
- book_obj.authors.add (*列表)
- book_obj.authors.remove() # 将某个特定的对象从被关联对象集合中去除
- book_obj.authors.clear() #清空被关联对象集合
- book_obj.authors.set(列表) #先清空再设置
3、反序列化保存一对一关联字段
- models.py
class Author(models.Model):
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
age = models.IntegerField()
author_detail = models.OneToOneField(to='AuthorDetail', to_field='nid', unique=True, on_delete=models.CASCADE)
class Meta:
db_table = 'author'
def author_dict(self):
return {'电话号码': self.author_detail.telephone, '生日': self.author_detail.birthday, '地址': self.author_detail.addr}
class AuthorDetail(models.Model):
nid = models.AutoField(primary_key=True)
telephone = models.BigIntegerField()
birthday = models.DateField()
addr = models.CharField(max_length=64)
(1)重写create方法
- serializers.py
class AuthorSerializer(serializers.Serializer):
# 序列化和反序列化字段
name = serializers.CharField()
age = serializers.IntegerField()
# 序列化字段(在模型中定义方法)
author_dict = serializers.DictField()
# 重写create方法
def create(self, validated_data):
print(validated_data)
author_dict = validated_data.pop('author_dict')
author_detail = AuthorDetail.objects.create(**author_dict)
author_obj = Author.objects.create(**validated_data, author_detail=author_detail)
return author_obj
- 这里需要将
author_dict
里需要的字段全部在前端传入,例:
{
"name": "xuyuan",
"age": 23,
"author_dict": {
"电话号码": 150,
"生日": "2001-12-12",
"地址": "芜湖"
}
}
(2)重写update方法
-
前面的都不需要动,只需重写update方法
-
views.py
def put(self, request, pk):
author_obj = models.Author.objects.filter(pk=pk).first()
if not author_obj:
return Response('没有此作者')
author_ser = AuthorSerializer(instance=author_obj, data=request.data)
if author_ser.is_valid():
author_ser.save()
return Response(author_ser.data)
else:
return Response(author_ser.errors)
- serializers.py
def update(self, instance, validated_data):
# 与create相同,要在中间表更新数据
author_dict = validated_data.pop('author_dict')
AuthorDetail.objects.filter(pk=instance.author_detail.pk).update(**author_dict)
# 其他字段不变,author_detail_id字段不上传,因为是一对一绑定关系所以没必要
instance.name = validated_data.get('name')
instance.age = validated_data.get('age')
instance.save()
return instance
instance.author_detail.pk
:instance
对象获取到中间表author_detail
的pk
4、注意事项
# 不要有误区---> 如果增加图书,只是增加图书,选择作者和出版社的字段应该使用id代替(传:id)
{name:书名,price:11,publish:{'name':'北京出版社',city:北京},authors:[{},{}]}
{name:书名,price:11,publish:2,authors:[1,2]}
- read_only、 write_only 控制序列化类中某些字段,只用来序列化或反序列化
- 重写updata和create,保存逻辑,我们自己写
- 视图类中 serializer = BookSerializer(instance=pk, data=request.data)
- 后续在序列化类中的update中def update(self, instance, validated_data):
- instance就是当时给的
十一、ModelSerializer类下的序列化和反序列化
-
之前写序列化类,没有显示指明跟哪个表一一对应
-
而
ModelSerializer
可以跟表做一一对应关系- 序列化类中,就不需要一个个写字段了–》跟表有对应关系
- 序列化类中,就不需要重写create和update
-
局部钩子全局钩子跟之前一样(注意层级)
1、模型表
- models.py
class Book(models.Model):
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
price = models.DecimalField(max_digits=5, decimal_places=2)
publish_date = models.DateField(auto_now=True)
publish = models.ForeignKey(to='Publish', to_field='nid', on_delete=models.CASCADE)
authors = models.ManyToManyField(to='Author')
def __str__(self):
return self.name
2、视图函数
- views.py
class BookView(APIView):
def post(self, request):
serializer = BookSerializer(data=request.data)
if serializer.is_valid():
print(serializer.validated_data) # 校验过后的数据
serializer.save()
return Response('成功')
else:
return Response({'code': 100, 'msg': serializer.errors})
class BookDetailView(APIView):
def put(self, request, pk):
obj = Book.objects.all().filter(pk=pk).first()
# 传入的instance是什么,到了 update中,instance就是什么
serializer = BookSerializer(instance=obj, data=request.data)
if serializer.is_valid():
serializer.save()
return Response('成功')
else:
return Response({'code': 100, 'msg': serializer.errors})
3、序列化类
- serializer.py
from .models import Book
class BookSerializer(serializers.ModelSerializer):
class Meta:
model=Book # 与模型表做一一对应
fields='__all__'
# fields=['id','name','publish_detail','authors_list'] # 有个坑,所有字段都必须要写!
extra_kwargs={
'name':{'max_length':8}, # 限制name不能超过8
'publish':{'write_only':True},
'authors':{'write_only':True},
}
# 可以重写字段--以重写的为准
# 这个字段用来做序列化
publish_detail = PublishSerializer(source='publish',read_only=True)
# 这个字段用来做序列化
authors_list = AuthorSerializer(source='authors', many=True,read_only=True)
# 不需要写create了,但是字段必须是:publish和authors
- 此时序列化类中就不需要一个个写字段了,
ModelSerializer
会自动跟表做对应关系 model
:需要对应的表fields
:用于指定需要被序列化的字段,__all__
为全部,['name', 'age']
为指定extra_kwargs
:类似钩子函数,将对应字段加上限制
ut(self, request, pk):
obj = Book.objects.all().filter(pk=pk).first()
# 传入的instance是什么,到了 update中,instance就是什么
serializer = BookSerializer(instance=obj, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(‘成功’)
else:
return Response({‘code’: 100, ‘msg’: serializer.errors})
## 3、序列化类
- serializer.py
```python
from .models import Book
class BookSerializer(serializers.ModelSerializer):
class Meta:
model=Book # 与模型表做一一对应
fields='__all__'
# fields=['id','name','publish_detail','authors_list'] # 有个坑,所有字段都必须要写!
extra_kwargs={
'name':{'max_length':8}, # 限制name不能超过8
'publish':{'write_only':True},
'authors':{'write_only':True},
}
# 可以重写字段--以重写的为准
# 这个字段用来做序列化
publish_detail = PublishSerializer(source='publish',read_only=True)
# 这个字段用来做序列化
authors_list = AuthorSerializer(source='authors', many=True,read_only=True)
# 不需要写create了,但是字段必须是:publish和authors
- 此时序列化类中就不需要一个个写字段了,
ModelSerializer
会自动跟表做对应关系 model
:需要对应的表fields
:用于指定需要被序列化的字段,__all__
为全部,['name', 'age']
为指定extra_kwargs
:类似钩子函数,将对应字段加上限制