DRF多表关联的序列化和反序列化
目录
- DRF多表关联的序列化和反序列化
- 序列化定制字段source
- 一对多的序列化
- 多表关联的序列化
- 方式1:在表模型中定义方法
- 方式2:定制返回格式SerializerMethodField
- 方式3:子序列化
- 多表关联的反序列化
- 反序列化保存一对多关联字段
- create
- update
- 反序列化保存多对多关联字段
- create
- ModelSerializer类下的序列化和反序列化
序列化定制字段source
# models.py
class Book(models.Model):
title = models.CharField(max_length=32, null=True)
price = models.IntegerField(null=True)
publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE, null=True)
author = models.ManyToManyField(to='Author', null=True)
# views.py
class book(APIView):
def get(self, request):
book_obj = models.Book.objects.all()
serializer = BookSerializer(instance=book_obj, many=True)
# serializers.py
class BookSerializer(serializers.Serializer):
book_name = serializers.CharField(source='title')
这是一个serializers的字段,当我要进行序列化时
book_name
决定了该字段在前端显示的名字source = 'title'
决定了返回给前端的内容是表中的book表中的title
字段属性- 因此只要source指定了属性值,
book_name
可以随便改名
一对多的序列化
表中的publish字段
# models.py
class Book(models.Model):
publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
class Publish(models.Model):
name = models.CharField(max_length=32, null=True)
email = models.EmailField(null=True)
serializers中的序列化
# serializer.py
publish_name = serializers.CharField(source='publish.name')
publish_email = serializers.EmailField(source='publish.email')
- 当publish是个对象时直接序列化只能打印它的字典对象
- 因此需要在source中写跨表查询
- 注意:所有类型都可以用
CharField
响应
响应结果:
多表关联的序列化
方式1:在表模型中定义方法
# models.py
class Book(models.Model):
title = models.CharField()
price = models.IntegerField()
publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
author = models.ManyToManyField(to='Author')
@property
def publish_detail(self):
return {'name': self.publish.name, 'email': self.publish.email}
@property
def author_list(self):
author_list = []
for i in self.author.all():
author_list.append({'name': i.name, 'age': i.age})
return author_list
class Publish(models.Model):
name = models.CharField(max_length=32,)
email = models.EmailField()
# serializers.py
class BookSerializer(serializers.Serializer):
book_name = serializers.CharField(source='title')
price = serializers.IntegerField()
publish_detail = serializers.DictField()
author_list = serializers.ListField()
publish_detail = serializers.DictField()
相当于调用了book表中的publish_detail
方法,返回的是一个对象
方式2:定制返回格式SerializerMethodField
# models.py
class Book(models.Model):
publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE, null=True)
author = models.ManyToManyField(to='Author', null=True)
# serializers.py
class BookSerializer(serializers.Serializer):
publish_dict = serializers.SerializerMethodField()
def get_publish_dict(self, obj):
return {'name': obj.publish.name, 'email': obj.publish.email}
author_list = serializers.SerializerMethodField()
def get_author_list(self, obj):
author_list = []
for i in obj.author.all():
author_list.append({'name': i.name, 'age': i.age})
return author_list
SerializerMethodField()
:允许你在序列化器中定义一个基于方法的字段- 方法名必须是
get_ +需要定制的序列化字段
obj
:相当于将后端定义的表对象传了过来,你可以在里面做跨表查询
方式3:子序列化
# models.py
class Book(models.Model):
publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
author = models.ManyToManyField(to='Author')
# serializers.py
class PublishSerializer(serializers.Serializer):
name = serializers.CharField()
email = serializers.EmailField()
class AuthorSerializer(serializers.Serializer):
name = serializers.CharField()
age = serializers.IntegerField()
class BookSerializer(serializers.Serializer):
publish = PublishSerializer()
author_detail = AuthorSerializer(source='author', many=True)
- 直接在serializers中新建PublishSerializer类,让publish字段调用这个类就能获取他的所有属性
publish
和author_detail
可以任意改名,如果改名的话需要source
参数对应表中的字段名,否则会报错- 因为
author
字段是一个列表,所以要在参数中加上many=True
多表关联的反序列化
反序列化保存一对多关联字段
create
# models.py
class Book(models.Model):
title = models.CharField(max_length=32, null=True)
price = models.IntegerField(null=True)
publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE, null=True)
publish
字段一对多关系绑定了Publish
表,因此在Book
表中显示的是publish_id
在反序列化校验Book对象时,django并不认识Publish
表,因此只会识别publish_id
,那么前端发送请求时也要传入publish_id
# serializers.py
class BookSerializer(serializers.Serializer):
book_name = serializers.CharField(source='title')
price = serializers.IntegerField()
publish_detail = PublishSerializer(source='publish', read_only=True)
publish_id = serializers.IntegerField(write_only=True)
def create(self, validated_data):
print(validated_data)
publish_id = validated_data.pop('publish_id')
book_obj = models.Book.objects.create(**validated_data, publish_id=publish_id)
return book_obj
在反序列化校验publish_id
时为了不影响原先的序列化校验,要在不需要反序列化校验的参数后面加入read_only=True
参数,同时publish_id
加入write_only
参数只作用于反序列化校验
read_only=True
:该字段只会用于序列化校验write_only=True
:该字段只会用于反序列化校验
重写create方法(重点)
-
此时前端传入的参数应为:
-
print(validated_data)
打印结果为:{'title': '三体', 'price': 900, 'publish_id': 2}
-
先不看代码来试一下,如果这是我直接
book_obj = models.Book.objects.create(**validated_data)
会发生什么?
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)
# 这种方法和上面的含义相同,用其中一种即可
book_obj = models.Book.objects.create(title=validated_data.get('title'), price=validated_data.get('price'), publish_id=publish_id)
- 传入成功应正确返回序列化后的字段
update
# views.py
def put(self, request, u_id):
book_obj = models.Book.objects.filter(pk=u_id).first()
# 改对象必须传data和instance
serializer = BookSerializer(instance=book_obj, data=request.data)
if serializer.is_valid():
serializer.save()
return Response({'code': '200', 'msg': "修改成功", 'result': serializer.data})
else:
return Response({'code': '201', 'msg': serializer.errors})
# serializers.py
def update(self, instance, validated_data):
instance.publish_id = validated_data.get('publish_id')
instance.title = validated_data.get('title')
instance.price = validated_data.get('price')
instance.save()
return instance
- updata相比于create就多了个instance参数
instance
就是views层传过来的模型对象,将要修改的字段保存为validated_data
中的数据即可
反序列化保存多对多关联字段
create
首先定义一个author
字段,与Author表绑定多对多关系字段
class Book(models.Model):
title = models.CharField(max_length=32, null=True)
price = models.IntegerField(null=True)
author = models.ManyToManyField(to='Author', null=True)
class Author(models.Model):
name = models.CharField(max_length=10, null=True)
age = models.SmallIntegerField(null=True)
那么此时的数据便不会存在于Book
或Author
中,而是自动新建一个book_author
表,例如:
def create(self, validated_data):
author_id = validated_data.pop('author_id')
book_obj = models.Book.objects.create(**validated_data)
book_obj.author.add(author_id)
return book_obj
- 首先将
author_id
踢出,然后将其单独插入中间表book_author
,具体原因跟一对多关系相同
ModelSerializer类下的序列化和反序列化
# models.py
class Author(models.Model):
name = models.CharField(max_length=10, null=True)
age = models.SmallIntegerField(null=True)
author_detail = models.OneToOneField(to='AuthorDetail', on_delete=models.CASCADE, null=True)
# views.py
class author(APIView):
def get(self, request):
author_obj = models.Author.objects.all()
serializer = AuthorSerializer(instance=author_obj, many=True)
return Response({'code': '200', 'msg': '查询成功', 'result': serializer.data})
# serializer.py
from app.models import Author
class AuthorSerializer(serializers.ModelSerializer):
class Meta:
model = Author
fields = '__all__'
extra_kwargs = {
'name': {'max_length': 8}, # 限制name不能超过8
'author_detail': {'read_only': True},
}
- 此时序列化类中就不需要一个个写字段了,
ModelSerializer
会自动跟表做对应关系 model
:需要对应的表fields
:用于指定需要被序列化的字段,'__all__'
为全部['name', 'age']
为指定extra_kwargs
:类似钩子函数,将对应字段加上限制