序列化组件

【一】序列化器-Serializer

作用:

序列化:序列化器会把模型对象转换成字典,经过Response以后变成json字符串
反序列化:把客户端发送过来的数据,经过request以后变成字典,序列化器可以把字典转成模型
反序列化校验:完成数据校验功能

【1】序列化类快速使用(查看)

创建一个数据库模型类,并迁移数据库makemigrations,migrate

from django.db import models
​
​
# Create your models here.
class Student(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    gender = models.IntegerField(choices=((0, '未知'), (1, '男'), (2, '女')))

创建serializer.py文件(序列化器)

from rest_framework import serializers
​
​
# 使用步骤:
# 1 定义一个类,继承Serializer
# 2 在类中写要序列化的字段,这个字段和models中的字段一一对应,且不想显示哪一个字段就注释哪一个
# 3 在视图类中使用
​
class StudentSerializer(serializers.Serializer):
    id = serializers.IntegerField(required=False)
    name = serializers.CharField(required=True)
    age = serializers.IntegerField(required=True)
    gender = serializers.IntegerField(required=False, default=0)

在views.py文件中使用序列化类

from rest_framework.views import APIView
from rest_framework.response import Response
from django.views import View
from .models import Student
from .serializer import StudentSerializer
​
​
class StudentView(APIView):
    # 查看所有学生
    def get(self, request):
        res = Student.objects.all()
        # 方法一:麻烦 代码多
        # student = []
        # for l in res:
        #     student.append({'name': l.name, 'age': l.age, 'gender': l.get_gender_display()})
​
        # 方法二:drf提供的序列化类
        # 使用序列化类完成序列化 :instance 是要序列化的对象,如果是多条 many=True
        serializer = StudentSerializer(instance=res, many=True)
        # 如果是当个数据是data是字典,多条数据时他是列表套字典
        print(serializer.data)
        return Response(serializer.data)
    
    
class StudentViews(APIView):
    # 根据id查看对象
    def get(self, request, pk):
        student = Student.objects.filter(pk=pk).first()
        # 序列化
        serializer = StudentSerializer(instance=student)
        # 返回信息
        return Response(serializer.data)

【2】常用字段类

# 1 记住--》跟models中得 model.字段类, 一一对应
# 2 具体有哪些--带#的都是常用的需要记住
# 3 绝招:如果不知道models和Serializer如何对应
    序列化类统一用 CharField 
DateTimeField  #    
DateField     #
TimeField     #
IntegerField  #  
CharField   #   
BooleanField
NullBooleanField    
EmailField  
RegexField  
SlugField   
URLField    
UUIDField   
IPAddressField
FloatField
DecimalField    
ChoiceField 
MultipleChoiceField 
FileField   
ImageField  
# --------两个特殊的,很重要----
ListField
DictField   

【3】常用字段参数

# 1 序列化的 字段类在实例化时,传参数
    -作用是:为了反序列化校验用
    
# 2 有哪些字段参数--》了解
    # 只针对于:CharField
    max_length  最大长度
    min_lenght  最小长度
    allow_blank 是否允许为空
    trim_whitespace 是否截断空白字符    
    
    # 只针对于:IntegerField
    max_value   最小值
    min_value   最大值
    
    # 只针对于:DateTimeField 
    format:序列化时,显示的格式
    DateTimeField(format='Y-m-d')
    
    # 所有字段类都用
    required:是否必填   
    default :反序列化时使用的默认值
    allow_null  表明该字段是否允许传入None,默认False
    
    # 两个重点
    read_only   表明该字段仅用于序列化输出,默认False
    write_only  表明该字段仅用于反序列化输入,默认False

【4】反序列化保存(新增、修改、删除)

# 新增
# 检查传入的数据是否符合定义的字段类型和约束条件
1 写个序列化类,继承Serializer
2 在序列化类中写字段,和字段参数[校验用的]
3 在视图类中使
# viws.py
    def post(self, request):
        serializer = StudentSerializer(data=request.data)
        # 判断方法一:如果校验不通过,直接抛出异常,后面代码就不会执行了
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return Response({"code": 200, "msg": "添加成功"})
​
        # 判断方法二:if判断方法
        # # 检查传入的数据是否符合定义的字段类型和约束条件
        # if serializer.is_valid():
        #     # 校验通过 保存,需要在serializer文件中重写create方法
        #     serializer.save()
        #     # 返回新增的的数据
        #     return Response(serializer.data)
        # else:
        #     # 校验失败返回到前端
        #     return Response({"code": 301, "msg": serializer.errors})
​
# serializer.py
    # validated_data是前端传入的,然后在views中用is_valid()校验过的数据
    def create(self, validated_data):
        # 这里使用**validated_data传入的数据如果比表的字段多或 与表的字段不对应 是不行的
        # 如果少的话取决于少的字段是否可以为空
        student = Student.objects.create(**validated_data)
        return student
        # 这里需要返回student新增对象 后面增加完成后需要输出serializer.data使用
        
# 修改
# views.py
    def put(self, request, pk):
        # 找到要修改的学生对象
        student = Student.objects.filter(pk=pk).first()
        # 拿到前端传入的数据  要修改的instance 和 修改后的data
        serializer = StudentSerializer(instance=student, data=request.data)
        # 检查传入的数据是否符合定义的字段类型和约束条件
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
            # return Response({"code": 200, "msg": "修改成功"})
        else:
            return Response({"code": 302, "msg": "修改失败"})
        
# serializer.py
    def update(self, instance, validated_data):
        # instance 需要修改的对象
        # validated_data  前端传入的修改后的数据
        # 方法一:
        # instance.name = validated_data.get('name')
        # instance.age = validated_data.get('age')
        # instance.gender = validated_data.get('gender')
​
        # 方法二:
        # # 循环拿到字典的一个个key
        for key in validated_data:
            # 下面这句话等同于 instance.name = validated_data.get('name')
            setattr(instance, key, validated_data.get(key))
        instance.save()
        # 将修改后的对象返回出去
        return instance
​
# 删除
    def delete(self, request, pk):
        Student.objects.filter(pk=pk).delete()
        return Response({"code": 200, "msg": "删除成功"})

【5】看源码

# 1 为什么要重写 update和create
    因为执行serializer.save--->会触发self.create或self.update执行
    我们没写--》找父亲--》找父亲--》找到了save
    def create(self, validated_data):
        raise NotImplementedError('`create()` must be implemented.')
# 2 为什么调用的都是save,但是最后触发的序列化类中得方法是不同的?
    因为内部判断了 instance 是否为None
    
# 阅读源码
    -serializer.save()-->序列化类对象【自己写的StudentSerializer,没有save】 调用save,会触发父类的save--》Serializer中也没有save---》继续找父类BaseSerializer 有save方法
    -save源码
        # self 是咱们写的序列化类的对象
        # 如果是修改,instance 有值,没有值的情况就是添加
        if self.instance is not None:
            # self.update 是重写,在这之前里面是有数据的有值的
            self.instance = self.update(self.instance, validated_data)
        else:
            self.instance = self.create(validated_data)
        return self.instance

【6】反序列化校验

(1)第一层:字段自己的校验

  • 写在字段类的属性上

# serializer.py 

from rest_framework import serializers
class StudentSerializer(serializers.Serializer):
    id = serializers.IntegerField(required=False)
    name = serializers.CharField(required=True, max_length=6, min_length=2)
    age = serializers.IntegerField(required=True, max_value=30, min_value=2)
    gender = serializers.IntegerField(required=False, default=0,)
  • 补充方法(了解即可)

    • 在字段上使用 validators=[函数内存地址]

# 示例
name = serializers.CharField(max_length=6, min_length=2,validators=[函数内存地址])
【tips】:postman上报错都是英文的,将他调成中文
  • settings.py注册app

'rest_framework'
  • 修改参数

# LANGUAGE_CODE = 'en-us'
LANGUAGE_CODE = 'zh-hans'

# TIME_ZONE = 'UTC'
TIME_ZONE = 'Asia/Shanghai'

USE_I18N = True

USE_TZ = False

(2)局部钩子单独控制某个字段

   	# validate_字段名(self,字段名)
    def validate_name(self, name):
        if name.startswith("sb"):
            # 校验失败抛出异常
            raise ValidationError("姓名不能以sb开头")
        else:
            # 校验成功,返回name参数
            return name

(3)全局钩子,不是单独校验某个字段的,用来校验多个字段

    def validate(self, attrs):  # attrs 是前端传入 通过自己设置的 字段要求 和 局部钩子校验后的数据
        age = attrs.get("age")
        name = attrs.get("name")
        if str(age) == name:
            raise ValidationError("此二值不可以一致")
        else:
            return attrs

【7】序列化之source(定制返回方式一,不常用)

  • 在serializer.py文件中使用

# 使用方法一:
# 将原来的字段名name修改成user
	修改后的字段名 = serializers.CharField(required=True, max_length=6, min_length=2, source='表中原来的字段名')
    
    user = serializers.CharField(required=True, max_length=6, min_length=2, source='name')
    # name = serializers.CharField(required=True, max_length=6, min_length=2)
    
# 使用方法二:
# 在模型表models.py
    @property
    def new_name(self):
        return self.name + "_nb"
# 在序列化类中
    user = serializers.CharField(required=True, max_length=6, min_length=2, source='new_name')
    
# 结果:将原来字段name数据的后面加_nb将字段名修改成user
# 【补充】
	# 这样也是可以的
	new_name = serializers.CharField(required=True, max_length=6, min_length=2)
    
# 使用方法三:
	# 使用source定制,跨表查询定制
	user = serializers.CharField(required=True, max_length=6, min_length=2, source='publish.name')

【8】序列化之定制返回格式(使用book表做示例)

  • 创建模型表 models.py

# book数据表
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, "city": self.publish.city, "addr": self.publish.addr}


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


class Author(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    addr = models.CharField(max_length=32)
  • views.py

class BookView(APIView):
    def get(self, request):
        books = Book.objects.all()
        serializer = BookSerializer(books, many=True)
        return Response({"code": 200, "msg": "请求成功", "data": serializer.data})
  • serializer.py

# # 子序列话方法,可以自己选择需要输出的字段
class PublishSerializer(serializers.Serializer):
    name = serializers.CharField()
    addr = serializers.CharField()
    city = serializers.CharField()
   
# 子序列话方法,可以自己选择需要输出的字段
class AuthorSerializer(serializers.Serializer):
    id = serializers.IntegerField()
    name = serializers.CharField()
    age = serializers.IntegerField()
    addr = serializers.CharField()


#  ListField
#  DictField
class BookSerializer(serializers.Serializer):
    name = serializers.CharField()
    price = serializers.IntegerField()
    # 定制返回格式方式一:使用source (用的少)
    publish = serializers.CharField(source="publish.name")
    # 定制返回格式方式二:在表模型中写方法
    publish_detail = serializers.DictField()
    # 定制返回格式方式三:在序列化类中写方法 get_字段名
    publish = serializers.SerializerMethodField()
    #
    def get_publish(self, obj):
    #     # 这里的obj就是当前序列化得到的book
        return {"name": obj.publish.name, "city": obj.publish.city, "addr": obj.publish.addr}

    # 定制返回格式方式四:使用子序列化 当前序列化得到book对象  book.publish -->
    # 出版社对象 --》再使用PublishSerializer序列化类做序列化对象
    publish = PublishSerializer()
    
    # 【练习】通过上面的几种方法进行作者的多对多序列化
    # 练习1:在表模型中写方法
    author = serializers.ListField(source="author_list")

    # 练习2:在序列化类中写方法
    author_all = serializers.SerializerMethodField()

    def get_author_all(self, obj):
        author_all = obj.author.all()
        author_list = []
        for author in author_all:
            author_list.append({"name": author.name, "age": author.age})
        return author_list

    # 练习3:子序列化 因为作者是多个所以要添加many=True
    # 这里authors必须和模型表中的字段名一致(可以用source对字段名进行定制)
    authors = AuthorSerializer(many=True)

【9】多表关联的反序列化

  • serializer.py

class BookSerializer(serializers.Serializer):
    # 序列化查看和反序列化新增都需要用到
    name = serializers.CharField()
    price = serializers.IntegerField()

    # 只有在序列化查看的时候才用到,可以添加一个只读属性read_only=True
    publish_detail = serializers.DictField(read_only=True)
    author_list = serializers.ListField(read_only=True)

    # 只有在序列化添加的时候才用到,可以添加一个只写属性write_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":30,"publish_id":2,"authors":[1,2]}
        # 弹出作者"authors":[1,2]
        author = validated_data.pop("authors")
        book = Book.objects.create(**validated_data)
        # 作者 保存到 中间表中--->多对多关系,使用ManyToMany自动创建中间件,要借助于 ManyToMany 字段操作中间表
        # .clear()清空、.add()添加、.remove()删除
        book.authors.add(*author)
        return book
  • views.py (book视图类)

    def post(self, request):
        serializer = BookSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return Response({"code": 200, "msg": "添加成功", "data": serializer.data})
问题一:
# 在增加图书的时候,也增加出版社和作者--》肯定不用
	-前端传的数据:{"name":"三国演义","price":19,"publish":{"name":"上海出版社","addr":"上海"},"authors":[{},{}]}
    -到了create中:validated_data 就是传入的
问题二:
# 针对于 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}  #作者详情
问题三:
# 如果前端多传了数据:多了个addr,序列化类中没有
	-{"name":"三国演义","price":19,"publish_id":1,"authors":[1,2],"addr":"上海"}
# 到了create中,validated_data中得数据:{"name":"三国演义","price":19,"publish_id":1,"authors":[1,2]}

【10】完善查询单个、修改、删除 接口

  • serializer.py

  • 重写修改

    def update(self, instance, validated_data):
        instance.name = validated_data.get("name")
        instance.price = validated_data.get("price")
        instance.publish_id = validated_data.get("publish_id")
        author = validated_data.pop("authors")
        instance.authors.set(*author)
        return instance
  • views.py(视图类)

class BookViews(APIView):
    def get(self, request, pk):
        book = Book.objects.filter(pk=pk).first()
        serializer = BookSerializer(instance=book)
        return Response({"code": 200, "msg": "查询成功", "data": serializer.data})

    def put(self, request, pk):
        book = Book.objects.filter(pk=pk).first()
        serializer = BookSerializer(instance=book, data=request.data)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return Response({"code": 200, "msg": "修改成功", "data": serializer.data})

    def delete(self, request, pk):
        Book.objects.filter(pk=pk).delete()
        return Response({"code": 200, "msg": "删除成功"})

【11】ModelSerializer使用

  • 对比

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

class BookModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book
        # fields = "__all__"  # 表示book表中所有字段都在这写了一遍
        fields = ["name", "price", "publish", "authors", "publish_detail", "author_list"]  # 只要book表中的name和price
        # 字段参数限制 下面的代码相当于 name = serializers.CharField(max_length=8)
        extra_kwargs = {"name": {"max_length": 8}, "price": {"max_value": 1000},
                        "publish": {"write_only": True},
                        "authors": {"write_only": True},
                        "publish_detail": {"read_only": True},
                        "author_list": {"read_only": True},
                        }

# 序列类中写方法
class BookModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book
        # fields = "__all__"  # 表示book表中所有字段都在这写了一遍
        fields = ["name", "price", "publish", "authors", "publish_real", "author_all"]  # 只要book表中的name和price
        # 字段参数限制 下面的代码相当于 name = serializers.CharField(max_length=8)
        extra_kwargs = {"name": {"max_length": 8}, "price": {"max_value": 1000},
                        "publish": {"write_only": True},
                        "authors": {"write_only": True}
                        }

    publish_real = serializers.SerializerMethodField()

    def get_publish_real(self, obj):
        return {"name": obj.publish.name, "city": obj.publish.city, "addr": obj.publish.addr}

    author_all = serializers.SerializerMethodField()

    def get_author_all(self, obj):
        author_all = obj.authors.all()
        author_list = []
        for author in author_all:
            author_list.append({"name": author.name, "age": author.age})
        return author_list
    
    # 局部钩子用法
    def validate_name(self, name):
        if "sb" in name:
            raise serializers.ValidationError("名字不合法")
        else:
            return name
	
    # 全局钩子用法
    def validate(self, attrs):
        name = attrs.get("name")
        price = attrs.get("price")
        if name == str(price):
            raise serializers.ValidationError("不合法")
        else:
            return attrs
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值