62、 序列化高级用法之source、定制字段的两种方式、多表关联反序列化保存、其他校验、ModelSerializer使用

94 阅读8分钟

序列化高级用法之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})