drf-序列化(后篇)
目录
- 1 序列化高级用法之source(了解)
- 补充: on_delete参数
- source的三种用法
- 2 序列化高级用法之定制字段的两种方式(非常重要)
- 2.1 方式一
- 序列化类中用SerializerMethodField定制+get_字段名方法
- 2.2 方式二
- 序列化类:DictField()/ListField()
- 表模型中:跟自定义的字段名相同的方法名
- 2.1 方式一
- 3 多表关联反序列化保存
- 3.1 反序列化之保存
- 3.2 反序列化之修改
- 4 反序列化字段校验其他
- 5 ModelSerializer使用
- 总结
- 代码
1 序列化高级用法之source(了解)
补充: on_delete参数
# on_delete:
CASCADE:级联删除,只要删除publish,跟publish关联的book,全都被删除
SET_DEFAULT:只要删除publish,跟publish关联的book,的publish字段会变成默认值,一定要配合default使用
SET_NULL:只要删除publish,跟publish关联的book,的publish字段会变成空,一定要配合null=True使用
models.SET(add):括号中可以放个值,也可以放个函数内存地址,只要删除publish,跟publish关联的book,的publish字段会变成set设的值或执行函数后函数的返回值
| on_delete的值 | 表现 |
|---|---|
| on_delete=None | 删除关联表中的数据时,与models.CASCADE相同 |
| on_delete=models.CASCADE | 删除当前子表数据,与之关联的所有数据全部删除 |
| on_delete=models.DO_NOTHING | 删除当前子表数据, 不进行任何改变 |
| on_delete=models.PROTECT | on_delete=models.DO_NOTHING |
| on_delete=models.SET_NULL | 删除当前子表数据, 关联的数据变为null(前提需要null=True) |
| on_delete=models.SET_DEFAULT | 删除当前子表数据,, 关联的值设置为默认值(前提需要default='默认数据') |
| on_delete=models.SET(执行对象) | 删除关联数据,使用执行对象的返回值 |
source的三种用法
-1 修改前端看到的字段key值---> source指定的必须是对象的属性
book_name = serializers.CharField(source='name')
-2 修改前端看到的value值,---> source指定的必须是对象的方法
表模型中写方法
def sb_name(self):
return self.name + '_sb'
序列化类中
book_name = serializers.CharField(source='sb_name')
-3 可以关联查询(得有关联关系)
publish_name = serializers.CharField(source='publish.name')
代码:
from rest_framework import serializers
class BookSerializer(serializers.Serializer):
# 写的这些字段类的名字,必须是book对象中的属性,所以book_name会报错
# 如果不想报错,使用source指定book对象中的属性
# 第一种用法:放一个对象的属性
book_name = serializers.CharField(source='name') # 给前端看到的是 book_name
# name = serializers.CharField(source='name') # 坑,会报错
name1 = serializers.CharField() # 同一个字段,可以序列化多次,但一般不会写
price = serializers.CharField()
# 第二种用法:放一个对象的方法 book.hhh_name()--->真正的书名_hhh
name = serializers.CharField(source='hhh_name')
# 第三种:关联查询,拿出出版社的名字
publish_name = serializers.CharField(source='publish.name')
2 序列化高级用法之定制字段的两种方式(非常重要)
# 方式一:在序列化类中写
1 写一个字段,对应的字段类是:SerializerMethodField
2 必须对应一个 get_字段名的方法,方法必须接受一个obj,返回什么,这个字段对应的value就是什么
# 方式二:在表模型中写
1 在表模型中写一个方法(可以使用:property,也可以不使用),方法有返回值(字典,字符串,列表)
2 在序列化类中,使用DictField,CharField,ListField
2.1 方式一
序列化类中用SerializerMethodField定制+get_字段名方法
class BookSerializer(serializers.Serializer):
name = serializers.CharField()
price = serializers.CharField()
# 拿出出版社的id和名字和addr,放到一个字典中
# 方式一:SerializerMethodField来定制,如果写了这个,必须配合一个方法get_字段名,这个方法返回什么,这个字段的值就是什么
publish_data = serializers.SerializerMethodField()
def get_publish_data(self, book_obj):
print(book_obj) # 要序列化的book对象
return {'publish_name': book_obj.publish.name, 'publish_addr': book_obj.publish.addr}
# 练习:拿出所有作者的信息--> 多条 [{name:,phone:},{}]
author_data = serializers.SerializerMethodField()
def get_author_data(self, book_obj):
lst = []
for author in book_obj.authors.all():
lst.append({'name': author.name, 'phone': author.phone, 'email': author.author_detail.email})
# 这里数据一定要对应上,录入了几个作者就要录入几个作者详情
# AuthorDetail matching query does not exist.
# 我通过作者查询作者详情,出错是我自己少录入了一个作者详情,整个查询都会报错
return lst
2.2 方式二
序列化类:DictField()/ListField()
class BookSerializer(serializers.Serializer):
name = serializers.CharField()
price = serializers.CharField()
# 方式二:用DictField()/ListField()来定制,
# 必须到表模型中写一个方法,方法名必须跟 自定义的字段名 相同的方法名,这个方法返回什么,这个字段的value就是什么
publish_data = serializers.DictField()
author_data = serializers.ListField()
表模型中:跟自定义的字段名相同的方法名
class Book(models.Model):
name = models.CharField(max_length=32)
price = models.IntegerField()
publish = models.ForeignKey(to='Publish', on_delete=models.SET_NULL, null=True)
authors = models.ManyToManyField(to='Author')
def hhh_name(self):
return self.name + '_hhh'
@property
def publish_data(self):
# print(self) # 当前的book对象
return {'id': self.publish.pk, 'name': self.publish.name, 'addr': self.publish.addr}
def author_data(self):
lst = []
for author in self.authors.all():
lst.append({'name': author.name, 'phone': author.phone, 'email': author.author_detail.email})
return lst
3 多表关联反序列化保存
# 序列化和反序列化,用的同一个序列化类
-序列化的字段有:name,price , publish_detail,author_list
-反序列化字段:name,price ,publish,author
# 如果:
只序列化,加个参数:read_only=True
只反序列化,加个参数:write_only=True
既要序列化,又要反序列化:不需要加参数
3.1 反序列化之保存
视图类
class BookView(APIView):
def post(self, request):
ser = BookSerialzier(data=request.data)
if ser.is_valid():
ser.save()
return Response({'code': 100, 'msg': '成功'})
else:
return Response({'code': 100, 'msg': ser.errors})
序列化类
# 序列化类,即做序列化,又做反序列化,还做数据校验
class BookSerializer(serializers.Serializer):
# 即用来做序列化,又用来做反序列化
name = serializers.CharField(max_length=8, min_length=2)
price = serializers.CharField()
# 使用方式二的DictField()/ListField()来自定义字段定制
# 这俩,只用来做序列化,展示给前端的数据
publish_data = serializers.DictField(read_only=True)
author_data = serializers.ListField(read_only=True)
# 这俩,只用来做反序列化,接收前端的数据,Book类中的字段
# 出版社,form显示的是下拉框,前端传过来的数据是出版社id
publish_id = serializers.IntegerField(write_only=True)
# 作者,是多对多关系,用户可能选择多个,form显示的是复选框,前端传过来的也是作者id,是[1,2,3]形式
authors = serializers.ListField(write_only=True)
def create(self, validated_data): # {name:西游记,price:88,publish:1,authors:[1,2]}
# book = Book.objects.create(**validated_data) # 这样会出错,因为上述数据是两张表中的数据
# 执行上述命令的真实情况如下:
# book = Book.objects.create(name=validated_data.get('name'),price=validated_data.get('price'),publish_id=validated_data.get('publish'),authors=validated_data.get('authors'))
# 操作方式1:
# book = Book.objects.create(name=validated_data.get('name'), price=validated_data.get('price'), publish_id=validated_data.get('publish'))
# book.authors.add(*validated_data.get('authors'))
# 操作方式2:
authors = validated_data.pop('authors')
book = Book.objects.create(**validated_data)
book.authors.add(*authors)
book.save()
return book
3.2 反序列化之修改
视图类
class BookDetailView(APIView):
def put(self, request, pk):
book = Book.objects.filter(pk=pk).first()
ser = BookSerializer(instance=book, data=request.data)
if ser.is_valid():
ser.save()
return Response({'code': 100, 'msg': '修改成功', 'data': ser.data})
else:
return Response({'code': 100, 'msg': ser.errors})
序列化类
class BookSerializer(serializers.Serializer):
# 即用来做序列化,又用来做反序列化
name = serializers.CharField(max_length=8, min_length=2)
price = serializers.CharField()
# 使用方式二的DictField()/ListField()来自定义字段定制
# 这俩,只用来做序列化,展示给前端的数据
publish_data = serializers.DictField(read_only=True)
author_data = serializers.ListField(read_only=True)
# 这俩,只用来做反序列化,接收前端的数据,Book类中的字段
# 出版社,form显示的是下拉框,前端传过来的数据是出版社id
publish_id = serializers.IntegerField(write_only=True)
# 作者,是多对多关系,用户可能选择多个,form显示的是复选框,前端传过来的也是作者id,是[1,2,3]形式
# 针对于authors的检验,使用字段的参数是不好检验的,可以使用局部钩子来检验
authors = serializers.ListField(write_only=True)
def update(self, instance, validated_data):
# {"name":"西游记11","price":98,"publish_id":2,"authors":[2]}
authors = validated_data.pop('authors')
for item in validated_data:
setattr(instance, item, validated_data[item])
instance.authors.set(authors)
instance.save()
return instance
4 反序列化字段校验其他
# 视图类中调用:ser.is_valid()---> 触发数据的校验
-4层
-字段自己的:max_length,required。。。
-字段自己的:配合一个函数name = serializers.CharField(max_length=8,validators=[xxx]) # 可以在类的外面定义一个xxx函数,但不常使用
-局部钩子
-全局钩子
5 ModelSerializer使用
# 之前写的序列化类,继承了Serializer,写字段,跟表模型没有必然联系
class XXSerialzier(Serializer)
id=serializer.CharField()
name=serializer.CharField()
XXSerialzier既能序列化Book,又能序列化Publish
总结
# 现在学的ModelSerializer,表示跟表模型一一对应,用法跟之前基本类似
1 写序列化类,继承ModelSerializer
2 在序列化类中,再写一个类,必须叫
class Meta:
model=表模型
fields=[] # 要序列化的字段
3 可以重写字段,一定不要放在class Meta
-定制字段,跟之前讲的一样
4 自定制的字段,一定要在fields中注册一下
5 class Meta: 有个extra_kwargs,为某个字段定制字段参数
6 局部钩子,全局钩子,完全一致
7 大部分情况下,不需要重写 create和update了
代码
class BookSerialzier(serializers.ModelSerializer):
# 第一个好处,不用一个个写字段了,把表模型中得字段映射过来
class Meta:
model = Book # 指定表模型
# fields='__all__' # 把表模型中所有字段都映射过来
fields = ['name', 'price', 'publish_detail', 'author_list', 'publish', 'authors'] # 自定制的字段,一定要在这里注册一下
extra_kwargs = {
'name': {'max_length': 10, 'required': True},
'publish': {'write_only': True},
'authors': {'write_only': True}
}
# 自定制字段:publish_detail author_list 写法有两种,跟之前一模一样
# 序列化使用
publish_detail = serializers.DictField(read_only=True)
author_list = serializers.ListField(read_only=True)
# 反序列化新增
# 全局钩子和局部钩子,跟之前完全一样