序列化高级用法之source
导入
1.创建了5个表(图书管理的5个)
from django.db import models
# Create your models here.
class Book(models.Model):
name = models.CharField(max_length=32)
price = models.CharField(max_length=32)
publish = models.ForeignKey(to='Publish',on_delete=models.SET_NULL,null = True)
authors = models.ManyToManyField(to ='Author')
class Publish(models.Model):
name = models.CharField(max_length=32)
addr = models.CharField(max_length=32)
class Author(models.Model):
name = models.CharField(max_length=32)
phone = models.CharField(max_length=11)
author_detail = models.OneToOneField(to ='AuthorDetail',on_delete=models.CASCADE)
class AuthorDetail(models.Model):
email = models.CharField(max_length=32)
age = models.IntegerField()
"""
补充: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设的值或执行函数
models.DO_NOTHING:什么都不做,但它需要跟db_constraint=False配合,表示不建立外键约束,创建逻辑外键,不是物理外键
不建立物理外键的好处?增删查改数据快
缺点:容易出现脏数据
实际工作中,都不建立物理外键,都是建立逻辑外键
"""
2.利用APIView+序列化类+Response查询数据
# views.py
from rest_framework.views import APIView
from app01 import models
from .serializer import BookSerializer
from rest_framework.response import Response
class BookView(APIView):
def get(self,request):
book_list = models.Book.objects.all()
ser = BookSerializer(instance=book_list,many=True)
return Response({'code':100,'msg':'查询成功','data':ser.data})
class BookDetail(APIView):
def get(self,request,pk):
book_obj = models.Book.objects.filter(pk=pk).first()
ser = BookSerializer(instance=book_obj)
return Response({'code':100,'msg':'查询成功','data':ser.data})
# serializer.py
from rest_framework import serializers
class BookSerializer(serializers.Serializer):
name = serializers.CharField()
price = serializers.IntegerField()
3.需求:给前端看到的是 book_name--->借用source
class BookSerializer(serializers.Serializer):
book_name = serializers.CharField(source="name")
price = serializers.IntegerField()
ps:写的这些字段类的名字,必须是book对象中的属性,所以直接写book_name会报错--->如果不想报错,使用source指定book对象中的属性
source
用法一:放一个对象的属性
class BookSerializer(serializers.Serializer):
book_name = serializers.CharField(source="name")
price = serializers.IntegerField()
name = serializers.CharField(source='name') # 报错 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.
ps: 1.同一个字段,可以序列化多次,但一般不会写
2.souce后面放的属性不与前面的名字一样,会报错
用法二:放一个对象的方法
# 需求:在每一本书后面加'_nb'
class BookSerializer(serializers.Serializer):
book_name = serializers.CharField(source="name_nb")
price = serializers.IntegerField()
# models.py
class Book(models.Model):
name = models.CharField(max_length=32)
price = models.CharField(max_length=32)
publish = models.ForeignKey(to='Publish',on_delete=models.SET_NULL,null = True)
authors = models.ManyToManyField(to ='Author')
def name_nb(self):
return self.name+'_nb'
用法三:关联查询
# 需求:拿出出版社的名字
class BookSerializer(serializers.Serializer):
name = serializers.CharField()
price = serializers.IntegerField()
publish_name = serializers.CharField(source='publish.name')
总结
1.修改前端看到的字段key值 ---> source指定的必须是对象的属性
book_name = serializers.CharField(source='name')
2.修改前端看到的value值 ---> source指定的必须是对象的方法
1.表模型中写方法
def sb_name(self):
return self.name + '_sb'
2.序列化类中
book_name = serializers.CharField(source='sb_name')
3.可以关联查询(得有关联关系)
publish_name = serializers.CharField(source='publish.name')
序列化高级用法之定制字段的两种方式
方式一:在序列化类中写
SerializerMethodField来定制,如果写了这个,必须配合一个方法get_字段名,这个方法返回什么,这个字段的值就是什么
class BookSerializer(serializers.Serializer):
name = serializers.CharField()
price = serializers.IntegerField()
# 拿出出版社的id和名字和addr,放到一个字典中
publish_detail = serializers.SerializerMethodField()
# 拿出所有作者的信息-->多条 [{name:,phone:},{}]
author_list = serializers.SerializerMethodField()
def get_publish_detail(self,book):
print(book) # Book object (1) -->传进来的参数是一个要序列化的对象
return {'id':book.publish.pk,'addr':book.publish.name}
def get_author_list(self,book):
l1 =[]
for author in book.authors.all():
dic = {'id':author.pk,'name':author.name,'phone':author.phone}
l1.append(dic)
return l1
方式二:在表模型中写
使用DictField(),ListField(),CharField()等字段类,到表模型中写一个方法,方法名必须与序列化字段名一致,这个方法返回什么,这个字段的value就是什么
# serializers.py
class BookSerializer(serializers.Serializer):
name = serializers.CharField()
price = serializers.IntegerField()
# 拿出出版社的id和名字和addr,放到一个字典中
publish_detail = serializers.DictField()
# 拿出所有作者的信息-->多条 [{name:,phone:},{}]
author_list = serializers.ListField()
# models.py
class Book(models.Model):
name = models.CharField(max_length=32)
price = models.CharField(max_length=32)
publish = models.ForeignKey(to='Publish',on_delete=models.SET_NULL,null = True)
authors = models.ManyToManyField(to ='Author')
@property
def publish_detail(self):
return {'id':self.publish.pk,'addr':self.publish.name}
def author_list(self):
l1 = []
for author in self.authors.all():
dic = {'id':author.pk,'name':author.name,'phone':author.phone}
l1.append(dic)
return l1
总结
1.方式一:在序列化类中写
1.写一个字段,对应的字段类是:SerializerMethodField
2.必须对应一个 get_字段名的方法,方法必须接受一个obj,返回什么,这个字段对应的value就是什么
2.方式二:在表模型中写
1.在表模型中写一个方法(可以使用:property),方法有返回值(字典,字符串,列表)
2.在序列化类中,使用DictField,CharField,ListField
多表关联反序列化保存
class BookSerializer(serializers.Serializer):
name = serializers.CharField()
price = serializers.IntegerField()
publish_detail = serializers.DictField()
author_list = serializers.ListField()
问题:当序列化的时候,使用了以上四个字段(name,price,publish_detail,author_list)
当反序列化的时候,却使用:name,price,publish,author这四个字段
解决:使用write_only,read_only
eg:
class BookSerializer(serializers.Serializer):
# 既用来序列化,又用来反序列化
name = serializers.CharField()
price = serializers.IntegerField()
# 用来序列化
publish_detail = serializers.DictField(read_only=True)
author_list = serializers.ListField(read_only=True)
# 用来反序列化
publish = serializers.IntegerField(write_only=True)
authors = serializers.ListField(write_only=True)
反序列化之保存
# 创建数据
# 方式一
class BookSerializer(serializers.Serializer):
# 既用来序列化,又用来反序列化
name = serializers.CharField()
price = serializers.IntegerField()
# 用来序列化
publish_detail = serializers.DictField(read_only=True)
author_list = serializers.ListField(read_only=True)
# 用来反序列化
publish = serializers.IntegerField(write_only=True)
authors = serializers.ListField(write_only=True)
def create(self, validated_data):
print(validated_data) # OrderedDict([('name', '三国演义'), ('price', 66), ('publish', 1), ('authors', [1, 2])])
name = validated_data.get('name')
price = validated_data.get("price")
publish_id = validated_data.get('publish')
authors=validated_data.get('authors')
res =Book.objects.create(name = name,price=price,publish_id=publish_id)
res.authors.add(*authors)
return res
# 方式二:
class BookSerializer(serializers.Serializer):
# 既用来序列化,又用来反序列化
name = serializers.CharField()
price = serializers.IntegerField()
# 用来序列化
publish_detail = serializers.DictField(read_only=True)
author_list = serializers.ListField(read_only=True)
# 用来反序列化
publish_id = serializers.IntegerField(write_only=True)
authors = serializers.ListField(write_only=True)
def create(self, validated_data):
print(validated_data) # OrderedDict([('name', '三国演义'), ('price', 66), ('publish', 1), ('authors', [1, 2])])
authors = validated_data.pop('authors')
book = Book.objects.create(**validated_data)
book.authors.add(*authors)
return book
反序列化之修改
class BookSerializer(serializers.Serializer):
# 既用来序列化,又用来反序列化
name = serializers.CharField()
price = serializers.IntegerField()
# 用来序列化
publish_detail = serializers.DictField(read_only=True)
author_list = serializers.ListField(read_only=True)
# 用来反序列化
publish_id = serializers.IntegerField(write_only=True)
authors = serializers.ListField(write_only=True)
def update(self, book, validated_data):
print(validated_data) # OrderedDict([('name', '三国演义2'), ('price', 66), ('publish_id', 1), ('authors', [1])])
book.name = validated_data.get('name')
book.price = validated_data.get('price')
book.publish_id= validated_data.get('publish_id')
print(book.publish_id)
book.authors.set(validated_data.get('authors'))
book.save()
return book
# 优化:
def update(self, book, validated_data):
print(validated_data) # OrderedDict([('name', '三国演义2'), ('price', 66), ('publish_id', 1), ('authors', [1])])
authors = validated_data.pop('authors')
for item in validated_data:
setattr(book,item,validated_data[item])
book.authors.set(authors)
book.save()
return book
反序列化字段校验其他
1.在字段中添加validators选项参数,也可以补充验证行为
def about_django(value):
if 'django' not in value.lower():
raise serializers.ValidationError("图书不是关于Django的")
class BookInfoSerializer(serializers.Serializer):
"""图书数据序列化器"""
id = serializers.IntegerField(label='ID', read_only=True)
btitle = serializers.CharField(label='名称', max_length=20, validators=[about_django])
2. 4层校验
1.字段自己的:max_length,required,...
2.字段自己的:配合一个函数name = serializers.CharField(max_length=8,validators=[xxx])
3.局部钩子
4.全局钩子
ModelSerializer使用
1.之前写的序列化类,继承了Serializer,写字段,跟表模型没有必然联系
class XXSerialzier(Serializer)
id=serializer.CharField()
name=serializer.CharField()
ps:XXSerialzier既能序列化Book,又能序列化Publish
2.现在学的ModelSerializer,表示跟表模型一一对应,用法跟之前基本类似
1.写序列化类,继承ModelSerializer
2.在序列化类中,再写一个类,必须叫
class Meta:
model=表模型
fields=[] # 要序列化的字段
3.可以重写字段,一定不要放在class Meta
-定制字段,跟之前讲的一样
4.自定制的字段,一定要在fields中注册一下
5.class Meta: 有个extra_kwargs,为某个字段定制字段参数
6.局部钩子,全局钩子,完全一致
7.大部分情况下,不需要重写 create和update了
from .models import Book
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book # 指定表模型
# fileds = '__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)
# 反序列化新增-->直接在fields中注册,可添加额外的参数
作业
1.Publish的5个接口,ModelSerializer写
# views.py
class PublishView(APIView):
def get(self,request):
publish_list = models.Publish.objects.all()
ser = PublishSerislizer(instance=publish_list,many=True)
return Response({'code':100,'msg':'查询成功','data':ser.data})
def post(self,request):
ser = PublishSerislizer(data = request.data)
if ser.is_valid():
ser.save()
return Response({'code':100,'msg':'创建成功'})
else:
return Response({'code': 100, 'msg': '创建成失败', 'errors': ser.errors})
class PublishDetail(APIView):
def get(self,request,pk):
publish_obj = models.Publish.objects.filter(pk=pk).first()
ser = PublishSerislizer(instance=publish_obj)
return Response({'code': 100, 'msg': '查询成功', 'data': ser.data})
def put(self,request,pk):
publish_obj = models.Publish.objects.filter(pk=pk).first()
ser = PublishSerislizer(data = request.data,instance=publish_obj)
if ser.is_valid():
ser.save()
return Response({'code':100,'msg':'修改成功'})
else:
return Response({'code': 100, 'msg': '修改失败', 'errors': ser.errors})
# serializer.py
from rest_framework import serializers
from app01 import models
from rest_framework.exceptions import ValidationError
class PublishSerislizer(serializers.ModelSerializer):
class Meta:
model = models.Publish
fields = ['name','addr','book_list',"book_list"]
extra_kwargs = {
'name':{'max_length':10},
'addr':{'max_length':10}
}
# 查询出版的书籍,价格[{'name':'','price':..},{}]
book_list = serializers.ListField(read_only=True)
def validate_name(self,name):
if '东京' in name:
raise ValidationError('出版社不能含有东京')
return name
def validate(self, attrs):
name = attrs.get('name')
addr = attrs.get('addr')
if name == addr:
raise ValidationError('出版社名字和地址不能一样')
return attrs
2.author的5个接口(新增或修改author时,把author_detail的内容也新增进去)
# serializer.py
from rest_framework import serializers
from app01 import models
class AuthorSerializer(serializers.ModelSerializer):
class Meta:
model = models.Author
fields = ['name','phone','author_detail1','book_list','author_detail',"email","age"]
extra_kwargs={
'author_detail':{"default":0}
}
# 查询作者的详情信息{'emile':..,'age':18}
author_detail1 = serializers.DictField(read_only=True)
# 查处作者所出的书名,价格[{'name':..,'price':...}]
book_list = serializers.ListField(read_only=True)
email = serializers.CharField(write_only=True)
age = serializers.CharField(write_only=True)
def validate_phone(self,phone):
if len(phone) !=11:
raise ValidationError('必须11位')
return phone
def create(self, validated_data):
print(validated_data) # {'name': 'xiao', 'phone': '1808', 'author_detail': 0, 'email': '12@qq.com', 'age': '18'}
# models.Author.objects.create(**validated_data)
email = validated_data.pop('email')
age = validated_data.pop('age')
print(validated_data)
res = models.AuthorDetail.objects.create(email=email,age=age)
pk = res.pk
name = validated_data.get('name')
phone = validated_data.get('phone')
author_detail_id = pk
author = models.Author.objects.create(name=name,phone=phone,author_detail_id= author_detail_id)
return author
def update(self, instance, validated_data):
print(validated_data) # {'name': 'na1', 'phone': '1808', 'author_detail': 0, 'email': '12@qq.com', 'age': '20'}
validated_data['author_detail'] = instance.author_detail_id
print(validated_data['author_detail'])
email = validated_data.pop('email')
age = validated_data.pop('age')
author_detail = validated_data.pop('author_detail')
print(validated_data) # {'name': 'na1', 'phone': '1808'}
for item in validated_data:
setattr(instance,item,validated_data[item])
instance.save()
models.AuthorDetail.objects.filter(pk=author_detail).update(email=email,age=age)
return instance
# views.py
from django.shortcuts import render
# Create your views here.
from rest_framework.views import APIView
from app01 import models
from .serializer import AuthorSerializer
from rest_framework.response import Response
class AuthorViews(APIView):
def get(self,request):
authors_list = models.Author.objects.all()
ser = AuthorSerializer(instance=authors_list,many=True)
return Response({'code':100,'msg':'查询成功','data':ser.data})
def post(self,request):
ser = AuthorSerializer(data= request.data)
if ser.is_valid():
ser.save()
return Response({'code': 100, 'msg': '创建成功'})
else:
return Response({'code': 100, 'msg': '创建失败','errors':ser.errors})
class AuthorDetail(APIView):
def get(self,request,pk):
authors_obj = models.Author.objects.filter(pk=pk).first()
ser = AuthorSerializer(instance=authors_obj)
return Response({'code':100,'msg':'查询成功','data':ser.data})
def put(self,request,pk):
author_obj = models.Author.objects.filter(pk=pk).first()
ser = AuthorSerializer(data= request.data,instance=author_obj)
if ser.is_valid():
ser.save()
return Response({'code': 100, 'msg': '修改成功'})
else:
return Response({'code': 100, 'msg': '修改失败','errors':ser.errors})