【六】序列化类serializer多表
【0】多表数据准备
from django.db import models
# Create your models here.
class Book(models.Model):
name = models.CharField(max_length=64, verbose_name='书名')
price = models.DecimalField(max_digits=6, decimal_places=2, verbose_name='价格')
publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE, verbose_name='出版社外键')
authors = models.ManyToManyField(to='Author', verbose_name='作者外键')
class Publish(models.Model):
name = models.CharField(max_length=64, verbose_name='出版社名字')
addr = models.CharField(max_length=64, verbose_name='出版社地址')
class Author(models.Model):
name = models.CharField(max_length=64, verbose_name='作者名字')
detail = models.ForeignKey(to='AuthorDetail', on_delete=models.CASCADE, verbose_name='详情外键字段')
class AuthorDetail(models.Model):
phone = models.BigIntegerField(verbose_name='电话号码')
age = models.IntegerField(verbose_name='年龄')
【1】序列化字段对应表
- 模型表中的字段和序列化类中的字段都是对应的
字段 | 字段构造方式 |
---|---|
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=) |
【2】序列化字段参数
(1)通用字段参数
参数名称 | 说明 |
---|---|
read_only | 表明该字段仅用于序列化输出,默认False |
write_only | 表明该字段仅用于反序列化输入,默认False |
required | 表明该字段在反序列化时必须输入,默认True |
default | 反序列化时使用的默认值 |
validators | 该字段使用的验证器是个列表,列表中是方法的地址 |
error_messages | 包含错误编号与错误信息的字典 |
label | 用于HTML展示API页面时,显示的字段名称 |
help_text | 用于HTML展示API页面时,显示的字段帮助提示信息 |
(2)可选部分参数
参数名称 | 作用 |
---|---|
max_length | 字符最大长度 |
min_lenght | 字符最小长度 |
allow_blank | 字符是否允许为空 |
trim_whitespace | 字符是否截断空白字符 |
max_value | 数值类型最小值 |
min_value | 数值类型最大值 |
【3】两个重要参数
(1)read_only
-序列化
- 当某个字段被标记为
readonly
时,它意味着在序列化过程中,该字段的值可以被读取并包含在序列化结果中,但在反序列化过程中,该字段的值不能被设置。 - 换句话说,
readonly
字段是只读的,可以从对象中提取数据,但不能将数据写入对象。
(2)write_only
-反序列化
- 与
readonly
相反,writeonly
字段在序列化过程中是忽略的,其值不会被包含在序列化结果中。 - 但在反序列化过程中,该字段的值可以被设置。这意味着
writeonly
字段是只写的,不能从对象中提取它的值,但可以在创建或更新对象时设置它的值。
【4】序列化-source参数
- 这个参数用于序列化
(1)指定返回给前端的键名
- 在前面的使用方法中,需要将序列化表的字段名和模型表的字段名统一
- 在这里,只要添加了
source
参数那么就可以修改序列化类中的字段名- 序列化类中的字段名就是返回给前端的键名
name = serializers.CharField(source='name')
- 报错:
AssertionError: It is redundant to specify source='name' on field 'CharField' in serializer 'BookSerializer', because it is the same as the field name. Remove the source keyword argument.
- 如果出现这个报错,那就说明这个source字段是多余的
- 即字段名已经是模型表中的字段名了
(2)指定模型表的方法字段
- source还可以指定方法,当然不指定也是可以,只要序列化类的名字字段可以对应上模型层的方法名就可以
(3)联表查询
- source还可以联表查询
【5】序列化-联表查询(高级)
(1)SerializerMethodField
- 在序列化类中还有这个字段
SerializerMethodField
-
自定义给前端的名字 = serializers.SerializerMethodField()
-
def get_自定义给前端的名字(self, obj):
-
其中obj是当前的对象
-
# 序列化类
class BookSerializer(serializers.Serializer):
publish_detail = serializers.SerializerMethodField()
def get_publish_detail(self, obj):
return {"id": obj.publish.pk, "name": obj.publish.name, "addr": obj.publish.addr}
# 结果
{
"publish_detail": {
"id": 2,
"name": "南方出版社",
"addr": "南方"
}
}
(2)DictField()和ListField()
- 前面的重心在序列化类中,
- 用这个两个字段就需要将重心放在模型层了
- 模型编写方法:可以是一对多还可以是多对多字段,只是在序列化类中使用的字段有所差别,多对多需要需要在序列化层中使用ListField,一对多需要在序列化层中使用DictField
(3)子序列化
-
在序列化类中是可以使用其他序列化类的
-
方法一:较复杂
- 同样的首先使用serializers.SerializerMethodField()
- 然后定义get_xxx方法,在方法中使用其他的序列化类得到结果,用法和在模型层的用法一致
-
方法二:简单
- 直接在序列化来中使用其他序列化类,只需要指定source即可
# 序列化类
from rest_framework import serializers
class AuthorSerializer(serializers.Serializer):
pk = serializers.IntegerField(source='id')
name = serializers.CharField()
class PublishSerializer(serializers.Serializer):
pk = serializers.IntegerField(source='id')
name = serializers.CharField()
addr = serializers.CharField()
class BookSerializer(serializers.Serializer):
# 方法二
publish_detail = PublishSerializer(source='publish')
# 方法一
authors_detail = serializers.SerializerMethodField()
def get_authors_detail(self, obj):
authors = obj.authors.all()
authors_ser = AuthorSerializer(instance=authors, many=True)
return authors_ser.data
- 结果
【6】反序列化-检验数据
- 在单表序列化中已经讲过了这里就总结性的叙述
- 当视图层调用.is_valid()时,开始校验数据
- 首先是字段参数校验(比如:max_value、…)
- 然后是特殊字段参数
validators
(这是一个列表,需要传入方法地址) - 局部钩子验证单个字段(
validate_<field_name>(self, <file_name>)
方法) - 全局钩子验证多个字段(
validate(self, obj)
方法)
【7】反序列化-保存数据
- 无论是多表还是单表都需要重写create方法,并返回保存的数据
def create(self, validated_data):
return obj
(1)多表传入是id
-
在这个例子中,一本书的创建,需要考虑到多个外键字段
- 首先是一对多的出版社外键字段,这个字段传入的应该是对应出版社的
id
,并且是单个数字 - 然后是多对多的作者外键字段,这个字段传入的应该也是对应作者的id,不过这里是列表
- 首先是一对多的出版社外键字段,这个字段传入的应该是对应出版社的
-
但是在序列化类中,如果直接进行合法数据的打散传入是不对的,因为需要传入的是对象,所以需要先将这些数据弹出,然后另外保存外键信息
from rest_framework import serializers
from book.models import Book
class AuthorSerializer(serializers.Serializer):
name = serializers.CharField()
class PublishSerializer(serializers.Serializer):
name = serializers.CharField()
addr = serializers.CharField()
class BookSerializer(serializers.Serializer):
name = serializers.CharField()
price = serializers.DecimalField(max_digits=6, decimal_places=2)
publish_detail = PublishSerializer(source='publish', read_only=True)
authors_detail = AuthorSerializer(source='authors', many=True, read_only=True)
publish = serializers.IntegerField(write_only=True)
authors = serializers.ListField(write_only=True)
def create(self, validated_data):
print(validated_data)
# 首先要弹出无法直接保存的数据
publish_id = validated_data.pop('publish')
authors_l = validated_data.pop('authors')
# 创建书籍
book_obj = Book.objects.create(**validated_data, publish_id=publish_id)
# 创建书籍和作者的第三章表
book_obj.authors.set(authors_l)
return book_obj
- 结果
(2)多表传入是信息
- 这里以作者信息为例
- 在模型表的创建中,作者信息保存在了两个表中
- 一个表保存了作者的名字和详情的外键字段
- 另一个表保存了作者的详细信息
- 在前端传入的过程中是不可能分开传输的,即先传输名字,在传输其他信息,显示是不合理,所以这里通过一个路由接口,传递所有的信息并创建保存
# 序列化类
from book.models import Book, AuthorDetail, Author
class AuthorSerializer(serializers.Serializer):
name = serializers.CharField()
phone = serializers.IntegerField(source='detail.phone')
age = serializers.IntegerField(source='detail.age')
def create(self, validated_data):
print(validated_data) # {'name': 'lucy', 'detail': {'phone': 1594687523, 'age': 22}}
detail_obj = AuthorDetail.objects.create(**validated_data.pop('detail'))
author_obj = Author.objects.create(name=validated_data.pop('name'), detail=detail_obj)
return author_obj
# 模型层
class AuthorAPIView(APIView):
def post(self, request, *args, **kwargs):
author_ser = AuthorSerializer(data=request.data)
if author_ser.is_valid():
author_ser.save()
return Response({'code': 100, 'msg': '保存成功', 'data': author_ser.data})
return Response({'code': 1002, 'msg': '校验失败', 'err': author_ser.errors})
- 结果
【8】反序列-修改数据
- 无论是多表还是单表都需要重写update方法,并返回保存的数据
def update(self, instance, validated_data):
return instance
(1)多表传入是id
-
在这个例子中,一本书的修改,需要考虑到多个外键字段
- 首先是一对多的出版社外键字段,这个字段传入的应该是对应出版社的
id
,并且是单个数字 - 然后是多对多的作者外键字段,这个字段传入的应该也是对应作者的id,不过这里是列表
- 首先是一对多的出版社外键字段,这个字段传入的应该是对应出版社的
-
但是在序列化类中,如果直接进行合法数据的打散传入是不对的,因为需要传入的是对象,所以需要先将这些数据弹出,然后另外保存外键信息
# 序列化类
from rest_framework import serializers
from book.models import Book, AuthorDetail, Author
class AuthorSerializer(serializers.Serializer):
name = serializers.CharField()
phone = serializers.IntegerField(source='detail.phone')
age = serializers.IntegerField(source='detail.age')
def create(self, validated_data):
print(validated_data) # {'name': 'lucy', 'detail': {'phone': 1594687523, 'age': 22}}
detail_obj = AuthorDetail.objects.create(**validated_data.pop('detail'))
author_obj = Author.objects.create(name=validated_data.pop('name'), detail=detail_obj)
return author_obj
class PublishSerializer(serializers.Serializer):
name = serializers.CharField()
addr = serializers.CharField()
class BookSerializer(serializers.Serializer):
name = serializers.CharField()
price = serializers.DecimalField(max_digits=6, decimal_places=2)
publish_detail = PublishSerializer(source='publish', read_only=True)
authors_detail = AuthorSerializer(source='authors', many=True, read_only=True)
publish = serializers.IntegerField(write_only=True)
authors = serializers.ListField(write_only=True)
def update(self, instance, validated_data):
# 首先弹出无法直接保存的数据
publish_id = validated_data.pop('publish')
authors_l = validated_data.pop('authors')
# 修改书籍
for key, value in validated_data.items():
setattr(instance, key, value)
# 修改外键
instance.publish_id = publish_id
instance.authors.set(authors_l)
instance.save()
return instance
def create(self, validated_data):
# 首先要弹出无法直接保存的数据
publish_id = validated_data.pop('publish')
authors_l = validated_data.pop('authors')
# 创建书籍
book_obj = Book.objects.create(**validated_data, publish_id=publish_id)
# 创建书籍和作者的第三章表
book_obj.authors.set(authors_l)
return book_obj
# 模型层
class BookAPIView(APIView):
def put(self, request, *args, **kwargs):
pk = kwargs.get('pk')
book_obj = Book.objects.filter(pk=pk).first()
book_ser = BookSerializer(instance=book_obj, data=request.data)
if book_ser.is_valid():
book_ser.save() # 需要重写update方法
return Response({'code': 100, 'msg': '修改成功', 'data': book_ser.data})
return Response({'code': 1002, 'msg': '修改失败', 'err': book_ser.errors})
def post(self, request, *args, **kwargs):
book_ser = BookSerializer(data=request.data)
if book_ser.is_valid():
book_ser.save() # 需要重写create放啊
return Response({'code': 100, 'msg': '保存成功', 'data': book_ser.data})
return Response({'code': 1002, 'msg': '校验失败', 'err': book_ser.errors})
def get(self, request, *args, **kwargs):
book_queryset = Book.objects.all()
# 使用序列化类
book_ser = BookSerializer(instance=book_queryset, many=True)
return Response({'code': 100, 'msg': '查询成功', 'ser_results': book_ser.data})
- 结果
(2)多表传入是信息
- 这里以作者信息为例
- 在模型表的修改中,作者信息保存在了两个表中
- 一个表保存了作者的名字和详情的外键字段
- 另一个表保存了作者的详细信息
- 在前端传入的过程中是不可能分开传输的,即先传输名字,在传输其他信息,显示是不合理,所以这里通过一个路由接口,传递所有的信息并创建保存
# 序列化类
from rest_framework import serializers
from book.models import Book, AuthorDetail, Author
class AuthorSerializer(serializers.Serializer):
name = serializers.CharField()
phone = serializers.IntegerField(source='detail.phone')
age = serializers.IntegerField(source='detail.age')
def update(self, instance, validated_data):
print(validated_data) # {'name': 'tom', 'detail': {'phone': 1234850153, 'age': 99}}
for key, value in validated_data.pop('detail').items():
setattr(instance.detail, key, value)
instance.detail.save()
instance.name = validated_data.pop('name')
instance.save()
return instance
def create(self, validated_data):
print(validated_data) # {'name': 'lucy', 'detail': {'phone': 1594687523, 'age': 22}}
detail_obj = AuthorDetail.objects.create(**validated_data.pop('detail'))
author_obj = Author.objects.create(name=validated_data.pop('name'), detail=detail_obj)
return author_obj
# 模型层
class AuthorAPIView(APIView):
def post(self, request, *args, **kwargs):
author_ser = AuthorSerializer(data=request.data)
if author_ser.is_valid():
author_ser.save()
return Response({'code': 100, 'msg': '保存成功', 'data': author_ser.data})
return Response({'code': 1002, 'msg': '校验失败', 'err': author_ser.errors})
def get(self, request, *args, **kwargs):
author_queryset = Author.objects.all()
# 使用序列化类
author_ser = AuthorSerializer(instance=author_queryset, many=True)
return Response({'code': 100, 'msg': '查询成功', 'ser_results': author_ser.data})
def put(self, request, *args, **kwargs):
pk = kwargs.get('pk')
author_obj = Author.objects.filter(pk=pk).first()
author_ser = AuthorSerializer(instance=author_obj, data=request.data)
if author_ser.is_valid():
author_ser.save() # 需要重写update方法
return Response({'code': 100, 'msg': '修改成功', 'data': author_ser.data})
return Response({'code': 1002, 'msg': '修改失败', 'err': author_ser.errors})
- 结果