反序列化之校验,序列化定制返回格式,多表关联反序列化,ModelSerializer的使用

Ⅰ 反序列化之校验

【一】 使用mysql 8.0

【1】django 的4版本以后,不支持mysql 5.7了,必须要8以上

  • 必须装mysqlclient:pip3 install mysqlclient
  • 或者使用pymysql,得写改两句话

【2】mysql 多版本共存

  • 看我上一篇文章

【二】反序列化校验

【1】字段自己的校验

  • 写在字段类的属性上 -max_length...
name = serializers.CharField(max_length=8,min_length=2)
age = serializers.IntegerField(max_value=100,min_value=1)
# 或者是
使用 validators=[函数内存地址]  -->了解即可
根据函数内存地址校验

# 如
name = serializers.CharField(max_length=8,min_length=2, validators=[函数内存地址])

  • 若是想将提示英文改为中文
  • settings.py中修改
LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai'
USE_I18N = True
USE_TZ = False


#  以上只能修改djang原生的错误提示  还需要

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    "app01.apps.App01Config",
    
    
    'rest_framework',  # 将rest_framework 注册到app下
]

【2】局部钩子校验

# :局部钩子校验:
    在序列化中写 validate_字段名
    如果校验通过,正常返回
    如果校验失败,就抛ValidationError
    需要引入  from rest_framework.exceptions import ValidationError
    
    
from rest_framework import serializers
from app01.models import Student
from rest_framework.exceptions import ValidationError

class StudentSerializer(serializers.Serializer):
    name = serializers.CharField(max_length=8,min_length=2)
    age = serializers.IntegerField(max_value=100,min_value=1)

    def create(self, validated_data):
        # validated_data数据 如果比表的字段多 或者是 跟表的字段不对应  会报错
        # validated_data数据 如果比表的字段少 是否报错 取决于 该字段是否可以为空
        student = Student.objects.create(**validated_data)
        return student


    # 局部钩子校验 单独校验某个字段 validate_字段名  不能改
    def validate_name(self, name):
        if 'sb' in name:
            # 校验不通过就抛出异常
            raise ValidationError('名字中不能带sb')

        else:
            # 校验通过,返回名字数据   若是不返回 数据出不去 运行就会报错
            # 如果有特殊需要 就返回return name+‘xx’
            return name

    def validate_age(self, age):
        if age == 44:
            # 校验不通过就抛出异常
            raise ValidationError('年龄中不能是44')
        else:
            return age

【3】全局钩子

# 全局钩子--->不是用来单独校验某个字段,用来校验多个字段
    # 全局钩子validate是固定的  不能进行修改
    def validate(self, attrs):  # attrs 是前端传入,通过字段自己和局部钩子校验后的数据
        age = attrs.get('age')
        name = attrs.get('name')
        if str(age) == name:
            raise ValidationError('名字和年龄不能一样')
        else:
            return attrs

【4】代码实现

  • models.py
from django.db import models

# Create your models here.
from django.db import models


class Student(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()
  • views.py
from django.shortcuts import render
from rest_framework.response import Response

from rest_framework.views import APIView
from .models import Student
from .serializer import StudentSerializer


class StudentView(APIView):
    def post(self, request):
        serializer = StudentSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)  # 校验是否有异常  如果有就抛出异常  然后代码运行到此为止
        serializer.save()
        return Response({'code': 200, 'msg': 'ok'})
  • serializer.py
from rest_framework import serializers

from app01.models import Student

from rest_framework.exceptions import ValidationError

class StudentSerializer(serializers.Serializer):
    name = serializers.CharField(max_length=8,min_length=2)
    age = serializers.IntegerField(max_value=100,min_value=1)



    def create(self, validated_data):
        # validated_data数据 如果比表的字段多 或者是 跟表的字段不对应  会报错
        # validated_data数据 如果比表的字段少 是否报错 取决于 该字段是否可以为空
        student = Student.objects.create(**validated_data)
        return student



    # 局部钩子校验 单独校验某个字段 validate_字段名  不能改
    def validate_name(self, name):
        if 'sb' in name:
            # 校验不通过就抛出异常
            raise ValidationError('名字中不能带sb')

        else:
            # 校验通过,返回名字数据   若是不返回 数据出不去 运行就会报错
            # 如果有特殊需要 就返回return name+‘xx’
            return name



    def validate_age(self, age):
        if age == 44:
            # 校验不通过就抛出异常
            raise ValidationError('年龄中不能是44')

        else:

            return age

    # 全局钩子--->不是用来单独校验某个字段,用来校验多个字段
    # 全局钩子validate是固定的  不能进行修改
    def validate(self, attrs):  # attrs 是前端传入,通过字段自己和局部钩子校验后的数据
        age = attrs.get('age')
        name = attrs.get('name')
        if str(age) == name:
            raise ValidationError('名字和年龄不能一样')
        else:
            return attrs

Ⅱ 序列化定制返回格式

【一】 序列化定制之source(给字段重新命名)

【1】定制表中某个字段

user = serializers.CharField(max_length=8, min_length=2,source='name')

重新命名的字段名 = serializers.CharField(source='数据库中存在的需要重新命名的字段名')


# 若是还需要进行局部钩子校验   现在的字段名 就需要改成 重命名后的 字段名
    def validate_user(self, name):
        if 'sb' in name:
            # 校验不通过抛异常
            raise ValidationError('名字中不能带sb')
        else:
            # 校验通过,返回名字
            return name

【2】定制模型表中某个方法

# 表模型中
from django.db import models


class Student(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()

    @property  # 包装成数据属性
    def new_name(self):   # 括号内是不能传参数的   以后 对象.name 就变成了name_NB
        return self.name + '_NB'

(1)source='new_name'方法

  • 序列化类中
  • 然后返回的字段名 就变成了new_name
#  序列化类中
user = serializers.CharField(max_length=8,min_length=2,source='new_name')
  • 查看属性的时候就会变成名字后面加'_NB'

(2)直接放方法,原来是只放字段名 ,现在是也能直接放方法

#  序列化类中
# 方法 = serializers.CharField(max_length=8,min_length=2) 

new_name = serializers.CharField(max_length=8,min_length=2)

(3)跨表查询

# 跨表查询
publish_name = serializers.CharField(max_length=8, min_length=2,source='publish.name')
#  不用再 book.publish.name 这样获取publish_name  直接用 publish.name 获取名字

【二】一对多关系定制返回格式

【1】数据准备

  • models.py
from django.db import models
class Publish(models.Model):
    name = models.CharField(max_length=32)
    addr = models.CharField(max_length=32)
    city = models.CharField(max_length=32)


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)
    # 多对多关系,需要有中间件表
    # 但是django的orm帮咱们优化了-->不用手动创建中间件,使用ManyToMany可以自动创建出来
    # authors 不是表里的字段,它是一个中间表
    authors=models.ManyToManyField(to='Author')



class Author(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    addr = models.CharField(max_length=32)
  • views.py
from rest_framework.response import Response

from rest_framework.views import APIView
from .models import Book
from .serializer import BookSerialzier


class BookView(APIView):
    def get(self, request):
        books = Book.objects.all()
        serializer = BookSerialzier(books, many=True)
        return Response({'code': 100, 'msg': '查询成功', 'results': serializer.data})
  • serializer.py
from rest_framework import serializers
class BookSerialzier(serializers.Serializer):
    name = serializers.CharField()
    price = serializers.IntegerField()
    publish = serializers.CharField()
    authors = serializers.CharField()
    
    # 不清楚怎么配置时  直接用serializers.CharField() 肯定不会错
  • urls.py

from django.contrib import admin
from django.urls import path
from .views import StudentView,BookView
urlpatterns = [
    path('students/', StudentView.as_view()),
    path('books/', BookView.as_view()),
]

  • 数据库增加数据

  • 用postman查看所有数据

【2】想让publish和authors显示名字

(1)最简单的就是序列化类时用source(不太用)

publish = serializers.CharField(source='publish.name')

  • 但是想让authors是[{ }]格式

(2)在表模型中写方法

  • models.py
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)
    # 多对多关系,需要有中间件表
    # 但是django的orm帮咱们优化了-->不用手动创建中间件,使用ManyToMany可以自动创建出来
    # authors 不是表里的字段,它是一个中间表
    authors=models.ManyToManyField(to='Author')


    # 这里定义的是publish_detail
    def publish_detail(self):
        return {'name': self.publish.name, 'addr': self.publish.addr, 'city': self.publish.city}
  • 所以在serializer.py序列化类中 字段名不是publish而是publish_detail
# 序列化类中
class BookSerialzier(serializers.Serializer):
    name = serializers.CharField()
    price = serializers.IntegerField()
    
    #  字段名不是publish而是publish_detail
    publish_detail = serializers.CharField()
    
    authors = serializers.CharField()
  • 这种是返回的单引号引起来的 字符串 所以不能像publish_detail.属性名将数据取出来

  • 所以需要再序列化类中修改字段类型
class BookSerialzier(serializers.Serializer):
    name = serializers.CharField()
    price = serializers.IntegerField()
    
    # 将字段类型改为 DictField
    publish_detail = serializers.DictField()
    authors = serializers.CharField()
    
    # 下图就是修改好之后的  输出展示的是Jason格式字符串

(3)在序列化类中写方法

publisher = serializers.SerializerMethodField()

    # get_字段名
    def get_publish(self,obj):
        # obj 就是当前序列化到得book对象
        return {'name':'xx','addr':'yy'}
    
    # 但是这样会将数据写死

  • 将数据动态表示
# 序列化类中
publish = serializers.SerializerMethodField()

    # get_字段名
    def get_publish(self,obj):
        # obj 就是当前序列化到得book对象
        return {'name':obj.publish.name,'addr':obj.publish.addr,'city':obj.publish.city}

(4)使用子序列化

# 使用子序列化  字段名必须是publish  只有book.publish才是所需要的对象

# 当前序列化到得book对象  book.publish-->出版社对象--->使用PublishSerializer序列化类做序列化
publish = PublishSerializer()

class PublishSerializer(serializers.Serializer):
    name = serializers.CharField()
    # addr = serializers.CharField()  # 不想展示直接不写或者是注释掉
    city = serializers.CharField()

【三】多对多关系,定制返回格式

【1】表模型中写方法

   # 在模型表中
   def author_detail(self):
        # 拿出所有作者
        authors = self.authors.all()
        author_list = []
        for author in authors:
            author_list.append({'name': author.name, 'age': author.age})  # 跨表查询  直接需要连的表.属性就行

        return author_list
    
    
    # 在序列化类中
     author_detail = serializers.CharField()
        # 这种拿到的不是列表套字典,是字符串数据  所以要修改类型 

  • 修改类型
 # 修改类型  ListField
 
 
 author_detail = serializers.ListField()

【2】在序列化类中写方法

author_all = serializers.SerializerMethodField()
    def get_author_all(self,obj):
        authors = obj.authors.all()
        l = []
        for author in authors:
            l.append({'name': author.name, 'age': author.age,'id':author.id})  # 跨表查询  直接需要连的表.属性就行

        return l

【3】子序列化

# 在序列化类中

# 这里的字段名必须是你models上面的字段名字
 authors = AuthorSerializer(many=True)
    
    
class AuthorSerializer(serializers.Serializer):
    name = serializers.CharField()
    age = serializers.IntegerField()  

【四】总的代码

  • models.py
from django.db import models

# Create your models here.
from django.db import models


class Student(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()

    @property  # 包装成数据属性
    def new_name(self):   # 括号内是不能传参数的   以后 对象.name 就变成了name_NB
        return self.name + '_NB'





class Publish(models.Model):
    name = models.CharField(max_length=32)
    addr = models.CharField(max_length=32)
    city = models.CharField(max_length=32)


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)
    # 多对多关系,需要有中间件表
    # 但是django的orm帮咱们优化了-->不用手动创建中间件,使用ManyToMany可以自动创建出来
    # authors 不是表里的字段,它是一个中间表
    authors=models.ManyToManyField(to='Author')


    def publish_detail(self):
        return {'name': self.publish.name, 'addr': self.publish.addr, 'city': self.publish.city}

    # def author_detail(self):
    #     # 拿出所有作者
    #     authors = self.authors.all()
    #     author_list = []
    #     for author in authors:
    #         author_list.append({'name': author.name, 'age': author.age})  # 跨表查询  直接需要连的表.属性就行
    #
    #     return author_list


class Author(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    addr = models.CharField(max_length=32)
  • serializer.py
from rest_framework import serializers

from app01.models import Student

from rest_framework.exceptions import ValidationError

class StudentSerializer(serializers.Serializer):
    new_name = serializers.CharField(max_length=8,min_length=2)
    age = serializers.IntegerField(max_value=100,min_value=1)



    def create(self, validated_data):
        # validated_data数据 如果比表的字段多 或者是 跟表的字段不对应  会报错
        # validated_data数据 如果比表的字段少 是否报错 取决于 该字段是否可以为空
        student = Student.objects.create(**validated_data)
        return student


    # 局部钩子校验 单独校验某个字段 validate_字段名  不能改
    def validate_user(self, name):
        if 'sb' in name:
            # 校验不通过就抛出异常
            raise ValidationError('名字中不能带sb')

        else:
            # 校验通过,返回名字数据   若是不返回 数据出不去 运行就会报错
            # 如果有特殊需要 就返回return name+‘xx’
            return name


    def validate_age(self, age):
        if age == 44:
            # 校验不通过就抛出异常
            raise ValidationError('年龄中不能是44')

        else:

            return age

    # 全局钩子--->不是用来单独校验某个字段,用来校验多个字段
    # 全局钩子validate是固定的  不能进行修改
    def validate(self, attrs):  # attrs 是前端传入,通过字段自己和局部钩子校验后的数据
        age = attrs.get('age')
        name = attrs.get('name')
        if str(age) == name:
            raise ValidationError('名字和年龄不能一样')
        else:
            return attrs



class AuthorSerializer(serializers.Serializer):
    name = serializers.CharField()
    age = serializers.IntegerField()




class PublishSerializer(serializers.Serializer):
    name = serializers.CharField()
    # addr = serializers.CharField()  # 不想展示直接不写或者是注释掉
    city = serializers.CharField()




class BookSerialzier(serializers.Serializer):
    name = serializers.CharField()
    price = serializers.IntegerField()
    # publish_detail = serializers.DictField()

    # publish = serializers.SerializerMethodField()
    #
    # # get_字段名
    # def get_publish(self,obj):
    #     return {'name':obj.publish.name,'addr':obj.publish.addr,'city':obj.publish.city}

    publish = PublishSerializer()

    # author_detail = serializers.ListField()


    # author_all = serializers.SerializerMethodField()
    # def get_author_all(self,obj):
    #     authors = obj.authors.all()
    #     l = []
    #     for author in authors:
    #         l.append({'name': author.name, 'age': author.age,'id':author.id})  # 跨表查询  直接需要连的表.属性就行
    #
    #     return l

    authors = AuthorSerializer(many=True)

Ⅲ 多表关联反序列化

【一】多表关联反序列化实现

# 多表关联的序列化

	soruce
    表模型中写方法
    序列化类中写方法
    子序列化
    

#  多表关联反序列化

	新增图书
    
    
# 序列化和反序列化 字段就不一样了  所以要重新写字段  

# 因为传数据 在你选出版社和作者都是数字  所以要重写字段
  • serializer.py
# 序列化类


# 如果这个绕不过来,先用笨办法:序列化用一个类,反序列化用一个类
# 通过 read_only 和 write_only 来控制
# 哪些字段即做序列化,又做反序列化   什么都不写
# 哪些字段只做序列化  read_only=True
# 哪些字段只做反序列化  write_only=True
class BookSerialzier(serializers.Serializer):
    # 下面俩字段,反序列化和序列化都要用
    name = serializers.CharField()
    price = serializers.IntegerField()

    # 模型表中写的定制方式--> 以下的两个字段  只能用来做序列化
    publish_detail = serializers.DictField(read_only=True)
    author_detail = serializers.ListField(read_only=True)

    # 反序列化的字段, 要重新写  这下面两行只能用来做反序列化
    publish_id = serializers.IntegerField(write_only=True)
    authors = serializers.ListField(write_only=True)



    # 重写create方法
    def create(self, validated_data):
        # validated_data={"name":"三国演义","price":19,"publish":1,"authors":[1,2]}
        authors = validated_data.pop('authors')  # {"name":"三国演义","price":19,"publish":1}
        book = Book.objects.create(**validated_data)
        # 作者 保存到 中间表中--->多对多关系,使用ManyToMany自动创建中间件,要借助于 ManyToMany 字段操作中间表
        # book.authors.clear() # 清空
        # book.authors.add(*authors)
        # book.authors.remove(*authors)  # 删除关系
        book.authors.add(*authors)

        return book
  • views.py
from .models import Book
from .serializer import BookSerialzier


class BookView(APIView):
    def get(self, request):
        books = Book.objects.all()
        serializer = BookSerialzier(books, many=True)
        return Response({'code': 100, 'msg': '查询成功', 'results': serializer.data})


    def post(self, request):
        serializer = BookSerialzier(data=request.data)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return Response({'code': 100, 'msg': '新增成功', 'results': serializer.data})

【二】多表关联反序列化常见问题

【1】问题一

# 在增加图书的时候,也增加出版社和作者--》肯定不用
	前端传的数据:{"name":"三国演义","price":19,"publish":{"name":"上海出版社","addr":"上海"},"authors":[{}{}]}
    到了create中:validated_data 就是传入的
    
    将publish和authors用pop 弹出
    authors = validated_data.pop('authors')
    再将对应关系 对应上 
    book = Book.objects.create(**validated_data)
    再保存
    book.authors.add(*authors)

【2】问题二

# 针对于 Author和AuthorDetail的情况
前端传的数据:{"name":"张三","age":19,"author_detail":1}  -->基本不用
前端传的数据:{"name":"三国演义","age":19,"addr":地址,"gender":1}  -->通常用它
到了create中:validated_data 就是传入的{"name":"三国演义","age":19,"addr":地址,"gender":1} 
	{"name":"三国演义","age":19}  # 作者
     {"addr":地址,"gender":1}  #作者详情
        分别传到两表中

【3】问题三

# 如果前端多传了数据:多了个addr,序列化类中没有
	{"name":"三国演义","price":19,"publish_id":1,"authors":[1,2],"addr":"上海"}
# 到了create中,validated_data中得数据:{"name":"三国演义","price":19,"publish_id":1,"authors":[1,2]}

为什么会没有数据  因为 字段校验的时候没有这个字段  所以这个数据就不要了 

Ⅳ ModelSerializer的使用

# 之前写的序列化类,继承Serializer,跟表模型没有必然联系,才需要重写 create和update
	确定保存或修改到哪个表
    
#  ModelSerializer,跟表模型一一对应,
	可以不重写,特殊情况需要重写 create和update
    要序列化和反序列化的字段,可以通过表映射过来,也可以不写了

【一】基于ModelSerializer编写Book的序列化和反序列化

  • views.py
# 视图层

from .serializer import  BookModelSerializer

def post(self, request):
        serializer = BookModelSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return Response({'code': 100, 'msg': '新增成功', 'result': serializer.data})
  • serializer.py
class BookModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book

        fields = '__all__' # 表示把book所有字段,在这写一遍
        # 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')

        # 若是只想要某些字段
        # fields = ['name', 'publish'] # 只要book中的name,publish

        # 字段参数如何校验
        # 1 校验数据  2 写read_only和write_only
        extra_kwargs={'name': {'max_length': 8},'price': {'max_value': 100}}

        # 局部钩子,一定要写在BookModelSerializer 这一层
        def validate_name(self, name):
            return name
        # 同理,全局钩子一样
        # create和update 可以不用写了  ModelSerializer已经封装好了
  • 校验规则正常使用

  • 数据可以新增成功,但是代码又自动进行了一次序列化 故而pubilsh和authors又被序列化

【二】既能序列化,又能反序列化---表模型中写

# 既能序列化,又能反序列化---表模型中写
        fields = ['name', 'price', 'publish', 'authors', 'publish_detail', 'author_detail']

        extra_kwargs = {'name': {'max_length': 8}, 'price': {'max_value': 100},
                        'publish_detail': {'read_only': True},
                        'author_detail': {'read_only': True},
                        'publish': {'write_only': True},
                        'authors': {'write_only': True},
                        }

【三】序列化类中写方法

class BookModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book

        fields = ['name', 'price', 'publish', 'authors', 'publish_real', 'author_all']  # 新增的publish_real 也要注册
        extra_kwargs = {'name': {'max_length': 8}, 'price': {'max_value': 100},
                        'publish': {'write_only': True},
                        'authors': {'write_only': True},
                        }
    publish_real = serializers.SerializerMethodField(read_only=True)
    def get_publish_real(self, obj):
        return {'name': obj.publish.name, 'addr': obj.publish.name, 'city': obj.publish.city}

    author_all = serializers.SerializerMethodField(read_only=True)

    def get_author_all(self, obj):
        authors = obj.authors.all()
        l = []
        for author in authors:
            l.append({'name': author.name, 'age': author.age, 'id': author.id})

        return l

【四】总代码实现

  • views.py
from django.shortcuts import render
from rest_framework.response import Response

from rest_framework.views import APIView
from .models import Student
from .serializer import StudentSerializer, BookModelSerializer


class StudentView(APIView):
    def post(self, request):
        serializer = StudentSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)  # 校验是否有异常  如果有就抛出异常  然后代码运行到此为止
        serializer.save()
        return Response({'code': 200, 'msg': '创建成功'})



    def get(self, request):
        student = Student.objects.all()
        serializer = StudentSerializer(student, many=True)
        return Response({'code': 200, 'msg': '查询成功', 'results': serializer.data})


from .models import Book
from .serializer import BookSerialzier


class BookView(APIView):
    def get(self, request):
        books = Book.objects.all()
        serializer = BookSerialzier(books, many=True)
        return Response({'code': 100, 'msg': '查询成功', 'results': serializer.data})


    # def post(self, request):
    #     serializer = BookSerialzier(data=request.data)
    #     serializer.is_valid(raise_exception=True)
    #     serializer.save()
    #     return Response({'code': 100, 'msg': '新增成功', 'results': serializer.data})


    def post(self, request):
        serializer = BookModelSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return Response({'code': 100, 'msg': '新增成功', 'result': serializer.data})

  • models.py
from django.db import models

# Create your models here.
from django.db import models


class Student(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()

    @property  # 包装成数据属性
    def new_name(self):   # 括号内是不能传参数的   以后 对象.name 就变成了name_NB
        return self.name + '_NB'





class Publish(models.Model):
    name = models.CharField(max_length=32)
    addr = models.CharField(max_length=32)
    city = models.CharField(max_length=32)


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)
    # 多对多关系,需要有中间件表
    # 但是django的orm帮咱们优化了-->不用手动创建中间件,使用ManyToMany可以自动创建出来
    # authors 不是表里的字段,它是一个中间表
    authors=models.ManyToManyField(to='Author')


    def publish_detail(self):
        return {'name': self.publish.name, 'addr': self.publish.addr, 'city': self.publish.city}

    def author_detail(self):
        # 拿出所有作者
        authors = self.authors.all()
        author_list = []
        for author in authors:
            author_list.append({'name': author.name, 'age': author.age})  # 跨表查询  直接需要连的表.属性就行

        return author_list


class Author(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    addr = models.CharField(max_length=32)
  • serializer.py
from rest_framework import serializers

from app01.models import Student,Book

from rest_framework.exceptions import ValidationError

class StudentSerializer(serializers.Serializer):
    new_name = serializers.CharField(max_length=8,min_length=2)
    age = serializers.IntegerField(max_value=100,min_value=1)



    def create(self, validated_data):
        # validated_data数据 如果比表的字段多 或者是 跟表的字段不对应  会报错
        # validated_data数据 如果比表的字段少 是否报错 取决于 该字段是否可以为空
        student = Student.objects.create(**validated_data)
        return student


    # 局部钩子校验 单独校验某个字段 validate_字段名  不能改
    def validate_user(self, name):
        if 'sb' in name:
            # 校验不通过就抛出异常
            raise ValidationError('名字中不能带sb')

        else:
            # 校验通过,返回名字数据   若是不返回 数据出不去 运行就会报错
            # 如果有特殊需要 就返回return name+‘xx’
            return name


    def validate_age(self, age):
        if age == 44:
            # 校验不通过就抛出异常
            raise ValidationError('年龄中不能是44')

        else:

            return age

    # 全局钩子--->不是用来单独校验某个字段,用来校验多个字段
    # 全局钩子validate是固定的  不能进行修改
    def validate(self, attrs):  # attrs 是前端传入,通过字段自己和局部钩子校验后的数据
        age = attrs.get('age')
        name = attrs.get('name')
        if str(age) == name:
            raise ValidationError('名字和年龄不能一样')
        else:
            return attrs


class AuthorSerializer(serializers.Serializer):
    name = serializers.CharField()
    age = serializers.IntegerField()


class PublishSerializer(serializers.Serializer):
    name = serializers.CharField()
    # addr = serializers.CharField()  # 不想展示直接不写或者是注释掉
    city = serializers.CharField()


# 如果这个绕不过来,先用笨办法:序列化用一个类,反序列化用一个类
# 通过 read_only 和 write_only 来控制
# 哪些字段即做序列化,又做反序列化   什么都不写
# 哪些字段只做序列化  read_only=True
# 哪些字段只做反序列化  write_only=True
class BookSerialzier(serializers.Serializer):
    # 下面俩字段,反序列化和序列化都要用
    name = serializers.CharField()
    price = serializers.IntegerField()

    # 模型表中写的定制方式--> 以下的两个字段  只能用来做序列化
    publish_detail = serializers.DictField(read_only=True)
    author_detail = serializers.ListField(read_only=True)

    # 反序列化的字段, 要重新写  这下面两行只能用来做反序列化
    publish_id = serializers.IntegerField(write_only=True)
    authors = serializers.ListField(write_only=True)



    # 重写create方法
    def create(self, validated_data):
        # validated_data={"name":"三国演义","price":19,"publish":1,"authors":[1,2]}
        authors = validated_data.pop('authors')  # {"name":"三国演义","price":19,"publish":1}
        book = Book.objects.create(**validated_data)
        # 作者 保存到 中间表中--->多对多关系,使用ManyToMany自动创建中间件,要借助于 ManyToMany 字段操作中间表
        # book.authors.clear() # 清空
        # book.authors.add(*authors)
        # book.authors.remove(*authors)  # 删除关系
        book.authors.add(*authors)

        return book

class BookModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book

        # fields = '__all__' # 表示把book所有字段,在这写一遍
        # 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')

        # 若是只想要某些字段
        # fields = ['name', 'publish'] # 只要book中的name,publish

        # # 字段参数如何校验
        # # 1 校验数据  2 写read_only和write_only
        # extra_kwargs={'name': {'max_length': 8},'price': {'max_value': 100}}
        #
        # # 局部钩子,一定要写在BookModelSerializer 这一层
        # def validate_name(self, name):
        #     return name
        # # 同理,全局钩子一样
        # # create和update 可以不用写了  ModelSerializer已经封装好了


        # 既能序列化,又能反序列化---表模型中写
        # fields = ['name', 'price', 'publish', 'authors', 'publish_detail', 'author_detail']
        #
        # extra_kwargs = {'name': {'max_length': 8}, 'price': {'max_value': 100},
        #                 'publish_detail': {'read_only': True},
        #                 'author_detail': {'read_only': True},
        #                 'publish': {'write_only': True},
        #                 'authors': {'write_only': True},
        #                 }


        fields = ['name', 'price', 'publish', 'authors', 'publish_real', 'author_all']  # 新增的publish_real 也要注册
        extra_kwargs = {'name': {'max_length': 8}, 'price': {'max_value': 100},
                        'publish': {'write_only': True},
                        'authors': {'write_only': True},
                        }
    publish_real = serializers.SerializerMethodField(read_only=True)
    def get_publish_real(self, obj):
        return {'name': obj.publish.name, 'addr': obj.publish.name, 'city': obj.publish.city}

    author_all = serializers.SerializerMethodField(read_only=True)

    def get_author_all(self, obj):
        authors = obj.authors.all()
        l = []
        for author in authors:
            l.append({'name': author.name, 'age': author.age, 'id': author.id})

        return l

Ⅴ 补充

【一】字符长度和字节长度

# 补充
	字符长度--->   len(字符串)----> abc中国!--->6 个符号
    字节长度--->   len('abc中国!'.encode('utf-8'))--->字节长度  -->abc中国!-->10
    # 在utf-8中 一个中文是3个字节  

【二】反序列化校验—–字段类上的属性校验

def validate_name(name):
    # 一些校验条件
    pass


class StudentSerializer(serializers.Serializer):
    name = serializers.CharField(validator=[validate_name])
    #  validators=[函数1]

【三】在Meta中写了字段,又在序列化类中重写了,会以重写的为准

class BookModelSerializer(serializers.ModelSerializer):
    # name=serializers.CharField(max_length=32)
    name=serializers.CharField(max_length=8)
    # 你在这里重写了校验规则 最后的校验以你最后一个这个写的为准
    class Meta:
        model = Book

        fields = ['name', 'price', 'publish', 'authors', 'publish_real', 'author_all']  
        # 列表里面的name等 就相当于 name=serializers.CharField(max_length=32)
        
# 在Meta中写了字段,又在序列化类中重写了,会以重写的为准
class BookModelSerialzier(serializers.ModelSerializer):
        # 所有的标准,以新写的为准
        name=serializers.CharField(max_length=32)
        class Meta:
            model = Book
            fields = ['name']

【四】 depth使用

  • views.py
class BookView(APIView):
    def get(self, request):
        books = Book.objects.all()
        serializer = BookModelSerializer(books, many=True)
        return Response({'code': 100, 'msg': '查询成功', 'results': serializer.data})
  • serializer.py
class BookModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book

        fields = '__all__' # 表示把book所有字段,在这写一遍

  • depth暴力解除
class BookModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book

        fields = '__all__' # 表示把book所有字段,在这写一遍
        depth = 1   # 这个意思是表一层 是book.publish跨一层到publish   如果是跨两层就写depth = 2  那么depth值可以是1、2、3等,取决于你想预加载多深的关联
        # 不过他是将你的字段全部展出  不能再进行定制修改

posted on   silence^  阅读(9)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
点击右上角即可分享
微信分享提示