【5.0】DRF之序列化组件

【一】序列化组件介绍

  • 做序列化
  • 做反序列化
  • 在反序列化保存到数据库之前,做数据库校验

【1】介绍

  • DRF(Django REST framework)是一个用于构建基于 Django 的 Web API 的强大框架。
    • 在 DRF 中,序列化组件是其中一个核心组件,用于在 API 请求和响应中处理数据的转换和验证。
  • 序列化组件的主要功能是将复杂的数据结构(例如模型对象)转换为可以序列化(序列化为 JSON、XML 等格式)和传输的数据,并且能够根据所需的数据结构对传入的数据进行反序列化,还原为程序可操作的对象。

【2】重要特点和用法:

  1. 定义序列化类:在 DRF 中,我们需要创建一个继承自 serializers.Serializerserializers.ModelSerializer 的序列化类。这个类定义了要序列化和反序列化的字段和规则。

  2. 字段类型:序列化类中的字段用于指定数据结构中的字段以及其对应的类型。DRF 提供了多种字段类型,如字符串字段 (CharField)、整数字段 (IntegerField)、日期时间字段 (DateTimeField) 等。

  3. 嵌套关系:序列化类支持嵌套关系的处理,可以通过使用 Serializer 字段或 ModelSerializer 字段来表示两个模型之间的关联关系。

  4. 验证和转换:序列化组件提供了数据验证和转换的功能,可以在反序列化时验证输入数据的有效性,并对输入数据进行转换,使其符合定义的字段规则。

  5. 增加额外的字段和逻辑:在序列化类中,可以添加额外的字段和自定义方法,来处理复杂的业务逻辑,如计算属性和关联模型的反向查询等。

  6. 序列化输出:序列化组件将序列化类中定义的字段转换为指定的输出格式(JSON、XML 等),方便进行数据传输和展示。

  7. 反序列化输入:序列化组件能够将传入的数据反序列化为程序可操作的对象,以便进行进一步的处理和保存。

【3】小结

  • 序列化
    • 序列化器会把模型对象(queryset,单个对象)转换成字典
    • 经过response以后变成json字符串
  • 反序列化
    • 把客户端发送过来的数据,经过request.data以后变成字典
    • 序列化器可以把字典转成模型
  • 反序列化
    • 完成数据校验功能

【二】基本使用

【1】准备数据模型

from django.db import models


# Create your models here.
class Book(models.Model):
    name = models.CharField(max_length=32)
    price = models.IntegerField()

【2】写查询所有图书的接口

(APIView+序列化类)

(1)查询所有数据

  • 路由
path('books/', BookView.as_view()),
  • 视图
from django.shortcuts import render

# Create your views here
from rest_framework.views import APIView
from rest_framework.response import Response
from .serializers.my_serializer import BookSerializer

from app01 import models


class BookView(APIView):
    def get(self, request):
        book_list = models.Book.objects.all()
        # 使用序列化类完成序列化
        # instance : 实例(对象)
        # data : 数据
        # many=True : 如果是queryset对象,一般都是必须加
        # many=False : 如果是 .first() 一般不写,默认不写是 False
        book_ser = BookSerializer(instance=book_list, many=True)
        data = book_ser.data  # 将 book_ser 转成字典 -- 列表套字典
        print(f"data的数据是{data}", f"data的类型是{type(data)}")
        # data的数据是[OrderedDict([('id', 1), ('name', '西游记'), ('price', 999)])]
        # data的类型是<class 'rest_framework.utils.serializer_helpers.ReturnList'>

        return Response({"code": 1000, "msg": "查询成功", "data": data})
  • 序列化器
# -*-coding: Utf-8 -*-
# @File : my_serializer .py
# author: Chimengmeng
# blog_url : https://www.cnblogs.com/dream-ze/
# Time:2023/7/27
from rest_framework import serializers


class BookSerializer(serializers.Serializer):
    # 要序列化的字段
    id = serializers.IntegerField()
    name = serializers.CharField()
    price = serializers.IntegerField()
  • 后端返回给前端的数据
{
    "code": 1000,
    "msg": "查询成功",
    "data": [
        {
            "id": 1,
            "name": "西游记",
            "price": 999
        }
    ]
}

(2)查询单条数据

  • 路由
path('books/<int:pk>/', BookDetailView.as_view()),
  • 视图
class BookDetailView(APIView):
    def post(self, request, pk):
        book_list = models.Book.objects.filter(pk=pk).first()
        # 使用序列化类完成序列化
        # instance : 实例(对象)
        # data : 数据
        # many=True : 如果是queryset对象,一般都是必须加
        # many=False : 如果是 .first() 一般不写,默认不写是 False
        book_ser = BookSerializer(instance=book_list)
        data = book_ser.data  # 将 book_ser 转成字典 -- 列表套字典

        return Response({"code": 1000, "msg": "查询成功", "data": data})
  • 后端返回给前端的数据
{
    "code": 1000,
    "msg": "查询成功",
    "data": {
        "id": 1,
        "name": "西游记",
        "price": 999
    }
}

【3】小结

(1)制作序列化器

from rest_framework import serializers


class BookSerializer(serializers.Serializer):
    # 要序列化的字段
    id = serializers.IntegerField()
    name = serializers.CharField()
    price = serializers.IntegerField()

  • 自定义类并继承serializers.Serializer
  • 序列化器中的字段可以选择性添加
    • 使用哪个字段就将那个字段添加上去即可
    • 序列化器内的字段尽量跟模型表中的字段类型相匹配

(2)使用序列化器

class BookView(APIView):
    def get(self, request):
        book_list = models.Book.objects.all()
        # 使用序列化类完成序列化
        # instance : 实例(对象)
        # data : 数据
        # many=True : 如果是queryset对象,一般都是必须加
        # many=False : 如果是 .first() 一般不写,默认不写是 False
        book_ser = BookSerializer(instance=book_list, many=True)
        data = book_ser.data  # 将 book_ser 转成字典 -- 列表套字典
        print(f"data的数据是{data}", f"data的类型是{type(data)}")
        # data的数据是[OrderedDict([('id', 1), ('name', '西游记'), ('price', 999)])]
        # data的类型是<class 'rest_framework.utils.serializer_helpers.ReturnList'>

        return Response({"code": 1000, "msg": "查询成功", "data": data})
  • 参数

    • instance : 实例(对象)
    • data : 数据
    • many=True : 如果是queryset对象,一般都是必须加
    • many=False : 如果是 .first() 一般不写,默认不写是 False
  • 序列化的结果

    • data = book_ser.data
      • 将 book_ser 转成字典 -- 列表套字典
    • data的类型是
      • <class 'rest_framework.utils.serializer_helpers.ReturnList'>

【三】常用序列化字段和参数

【1】常用字段

字段 字段构造方式 详解
BooleanField BooleanField() 布尔字段用于存储和表示真/假值。构造方法不需要参数。
NullBooleanField NullBooleanField() 可空布尔字段是可以接受三个值的布尔字段:True、False和None(空值)。构造方法不需要参数。
CharField CharField(max_length=None, min_length=None, allow_blank=False, trim_whitespace=True) 字符字段用于存储短文本数据。max_length指定字符的最大长度,min_length指定最小长度。allow_blank指定是否允许为空值。trim_whitespace指定是否在保存数据前去除首尾的空格。
EmailField EmailField(max_length=None, min_length=None, allow_blank=False) Email字段用于存储和验证电子邮件地址。max_length指定最大字符长度,min_length指定最小长度。allow_blank指定是否允许为空值。
RegexField RegexField(regex, max_length=None, min_length=None, allow_blank=False) 正则表达式字段用于存储和验证符合特定模式的数据。regex指定正则表达式,max_length指定最大字符长度,min_length指定最小长度。allow_blank指定是否允许为空值。
SlugField SlugField(max_length=50, min_length=None, allow_blank=False) 正则字段,验证正则模式 [a-zA-Z0-9_-]+ Slug字段用于存储URL友好的文本标识符。max_length指定最大字符长度,min_length指定最小长度。allow_blank指定是否允许为空值。
URLField URLField(max_length=200, min_length=None, allow_blank=False) URL字段用于存储和验证URL地址。max_length指定最大字符长度,min_length指定最小长度。allow_blank指定是否允许为空值。
UUIDField UUIDField(format='hex_verbose') format: 1) 'hex_verbose'"5ce0e9a5-5ffa-654b-cee0-1238041fb31a" 2) 'hex'"5ce0e9a55ffa654bcee01238041fb31a" 3)'int' - 如: "1234567012312313134124512351145114" 4)'urn' 如: "urn:uuid:5ce0e9a5-5ffa-654b-cee0-1238041fb31a" UUID字段用于存储和验证通用唯一标识符。format参数指定UUID的格式。
IPAddressField IPAddressField(protocol='both', unpack_ipv4=False, **options) IP地址字段用于存储和验证IP地址。protocol参数指定所允许的IP地址协议类型,unpack_ipv4参数指定是否拆分IPv4地址。
IntegerField IntegerField(max_value=None, min_value=None) 整数字段用于存储整数值。max_value指定最大值,min_value指定最小值。
FloatField FloatField(max_value=None, min_value=None) 浮点数字段用于存储浮点数值。max_value指定最大值,min_value指定最小值。
DecimalField DecimalField(max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None) max_digits: 最多位数 decimal_palces: 小数点位置 十进制字段用于存储精确的十进制数值。max_digits指定最多位数,decimal_places指定小数点位置。coerce_to_string指定是否将值强制转化为字符串形式。max_value指定最大值,min_value指定最小值。
DateTimeField DateTimeField(format=api_settings.DATETIME_FORMAT, input_formats=None) 日期时间字段用于存储日期和时间。format参数指定日期时间的输出格式,input_formats参数指定输入格式。
DateField DateField(format=api_settings.DATE_FORMAT, input_formats=None) 日期字段用于存储日期。format参数指定日期的输出格式,input_formats参数指定输入格式。
TimeField TimeField(format=api_settings.TIME_FORMAT, input_formats=None) 时间字段用于存储时间。format参数指定时间的输出格式,input_formats参数指定输入格式。
DurationField DurationField() 持续时间字段用于存储一段时间的持续时间。构造方法不需要参数。
ChoiceField ChoiceField(choices) choices与Django的用法相同 选择字段用于存储和验证预定义选项中的一个值。choices参数指定可选的选项值。
MultipleChoiceField MultipleChoiceField(choices) 多选字段用于存储和验证多个预定义选项中的值。choices参数指定可选的选项值。
FileField FileField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL) 文件字段用于上传和保存文件。max_length指定文件名的最大长度,allow_empty_file指定是否允许为空文件。use_url指定是否使用文件的URL路径。
ImageField ImageField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL) 图片字段用于上传和保存图片文件。max_length指定文件名的最大长度,allow_empty_file指定是否允许为空文件。use_url指定是否使用图片的URL路径。
ListField ListField(child=, min_length=None, max_length=None) 列表字段用于存储和验证列表类型的数据。child参数指定列表中元素的类型,min_length指定最小长度,max_length指定最大长度。
DictField DictField(child=) 字典字段用于存储和验证字典类型的数据。child参数指定字典中value的类型。
  • 总结:常用字段

    IntegerField      
    CharField   
    DateTimeField  
    DecimalField
    
    ListField和DictField
    

【2】字段参数(校验数据)

(1)选项参数:(CharField,IntegerField)

参数名称 作用
max_length 最大长度
min_lenght 最小长度
allow_blank 是否允许为空
trim_whitespace 是否截断空白字符
max_value 最小值
min_value 最大值

(2)通用参数:

参数名称 说明
read_only 表明该字段仅用于序列化输出,默认False
write_only 表明该字段仅用于反序列化输入,默认False
required 表明该字段在反序列化时必须输入,默认True
default 反序列化时使用的默认值
allow_null 表明该字段是否允许传入None,默认False
  • 常用 参数

    read_only   
    write_only
    

【四】反序列化之校验(增加)

  • 序列化类
class BookSerializer(serializers.Serializer):
    # 要序列化的字段
    # required : 是否必填字段,默认为True,必填字段
    id = serializers.IntegerField(required=False)
    # allow_blank:传入的数据允许为空,默认不为空
    # max_length: 限制字符串的最大长度
    name = serializers.CharField(allow_blank=True,max_length=8)
    # max_value:限制最大值
    # min_value:限制最小值
    # error_messages:自定义错误信息
    price = serializers.IntegerField(max_value=100, min_value=10, error_messages={"error": "价格必须在10-100之间"})
  • 后端使用
# 新增
def post(self, request):
    # 前端传入数据,通过request.data取到数据,将数据保存到数据库中
    # 借助于序列化类,完成校验和反序列化
    # data : 前端传入的数据
    book_ser = BookSerializer(data=request.data)
    # 校验数据
    if book_ser.is_valid():
        # 三层:字段自己的校验/全局钩子校验/局部钩子校验
        # 校验通过,保存数据
        # validated_data:校验通过的数据
        # 新增字段限制条件 ---> 在序列化类中的字段中添加限制条件
        after_data = book_ser.validated_data
    else:
        print(book_ser.errors)  # 校验失败的错误
        return Response({"code": 100, "msg": "成功"})
  • 小结:反序列化有三层校验

    • 字段自己的:写的字段参数:required/max_length ...

    • 局部钩子:写在序列化类中的方法

      • 方法名必须是 validate_字段名
      def validate_name(self, name):
          if 'sb' in name:
              # 不合法,抛异常
              raise ValidationError('书名中不能包含sb')
          else:
              return name
      
    • 全局钩子:写在序列化类中的方法

      • 方法名必须是 validate
      def validate(self, attrs):
          price = attrs.get('price')
          name = attrs.get('name')
          if name == price:
              raise ValidationError('价格不能等于书名')
          else:
              return attrs
      
    • 只有三层都通过,在视图类中:

      • ser.is_valid(): 才是True,才能保存

【五】反序列化之保存(增加)

  • 新增接口
    • 数据校验后
    • 调用序列化类的 .save()方法
    • 同时还要在序列化类中重写 create 方法
  • 序列化类
class BookSerializer(serializers.Serializer):
    # 要序列化的字段
    # required : 是否必填字段,默认为True,必填字段
    id = serializers.IntegerField(required=False)
    # allow_blank:传入的数据允许为空,默认不为空
    # max_length: 限制字符串的最大长度
    name = serializers.CharField(allow_blank=True, max_length=8)
    # max_value:限制最大值
    # min_value:限制最小值
    # error_messages:自定义错误信息
    price = serializers.IntegerField(max_value=100, min_value=10, error_messages={"error": "价格必须在10-100之间"})

    def create(self, validated_data):
        # validated_data 校验过后的数据
        # **validated_data : 解耦,将数据打散
        book = models.Book.objects.create(**validated_data)
        return book
  • 后端使用
# 新增
def post(self, request):
    # 前端传入数据,通过request.data取到数据,将数据保存到数据库中
    # 借助于序列化类,完成校验和反序列化
    # data : 前端传入的数据
    book_ser = BookSerializer(data=request.data)
    # 校验数据
    if book_ser.is_valid():
        # 三层:字段自己的校验/全局钩子校验/局部钩子校验
        # 校验通过,保存数据
        # validated_data:校验通过的数据
        # 新增字段限制条件 ---> 在序列化类中的字段中添加限制条件
        after_data = book_ser.validated_data
        ser.save()  # 会保存数据,但是会报错,因为不知道要保存到哪张表中
        return Response({"code": 100, "msg": "成功"})
    else:
        print(book_ser.errors)  # 校验失败的错误
        return Response({"code": 100, "msg": "失败", "error": book_ser.errors})
  • 小结
    • 序列化类中重写 create 方法
    • 在后端逻辑中调用 save 方法
    • 如果调用create方法,校验通过的 validated_data 数据不会自动传入
    • 需要传入的数据 ser = BookSerializer(data=request.data)

【六】反序列化之保存(修改)

【1.0】指定字段修改数据

  • 序列化类
class BookSerializer(serializers.Serializer):
    # 要序列化的字段
    # required : 是否必填字段,默认为True,必填字段
    id = serializers.IntegerField(required=False)
    # allow_blank:传入的数据允许为空,默认不为空
    # max_length: 限制字符串的最大长度
    name = serializers.CharField(allow_blank=True, max_length=8)
    # max_value:限制最大值
    # min_value:限制最小值
    # error_messages:自定义错误信息
    price = serializers.IntegerField(max_value=100, min_value=10, error_messages={"error": "价格必须在10-100之间"})

    def create(self, validated_data):
        # validated_data 校验过后的数据
        # **validated_data : 解耦,将数据打散
        book = models.Book.objects.create(**validated_data)
        return book

    def update(self, instance, validated_data):
        # instance : 需要修改的对象 --- book
        # validated_data : 需要修改并且是校验过的数据
        instance.name = validated_data.get('name')
        instance.price = validated_data.get('price')

        # 修改完要保存数据
        instance.save()
  • 后端使用
class BookDetailView(APIView):
    def post(self, request, pk):
        book_list = models.Book.objects.filter(pk=pk).first()
        # 使用序列化类完成序列化
        # instance : 实例(对象)
        # data : 数据
        # many=True : 如果是queryset对象,一般都是必须加
        # many=False : 如果是 .first() 一般不写,默认不写是 False
        book_ser = BookSerializer(instance=book_list)
        data = book_ser.data  # 将 book_ser 转成字典 -- 列表套字典

        return Response({"code": 1000, "msg": "查询成功", "data": data})

    # 新增
    def put(self, request, pk):
        book = Book.objects.get(pk=pk)
        ser = BookSerializer(instance=book, data=request.data)
        if ser.is_valid():
            ser.save()  # 也会报错,需要重写 update 方法
            return Response({"code": 100, "msg": "修改成功"})
        else:
            return Response({"code": 100, "msg": "修改失败", "error": book_ser.errors})
  • 问题就是
    • 在重写 update 方法的时候,需要一个个的去取字段然后做修改
    • 当字段太多的时候,会大大影响效率

【2.0】多字段优化

class BookSerializer(serializers.Serializer):
    # 要序列化的字段
    # required : 是否必填字段,默认为True,必填字段
    id = serializers.IntegerField(required=False)
    # allow_blank:传入的数据允许为空,默认不为空
    # max_length: 限制字符串的最大长度
    name = serializers.CharField(allow_blank=True, max_length=8)
    # max_value:限制最大值
    # min_value:限制最小值
    # error_messages:自定义错误信息
    price = serializers.IntegerField(max_value=100, min_value=10, error_messages={"error": "价格必须在10-100之间"})

    def create(self, validated_data):
        # validated_data 校验过后的数据
        # **validated_data : 解耦,将数据打散
        book = models.Book.objects.create(**validated_data)
        return book

    def update(self, instance, validated_data):
        # instance : 需要修改的对象 --- book
        # validated_data : 需要修改并且是校验过的数据
        for item in validated_data:  # validated_data: {"name":"dream","age":18}
            # 利用反射语法
            setattr(instance, item, validated_data[item])
            # 等同于
            # setattr(book, "name", "dream")
            # setattr(book, "age", 18)

        # 修改完要保存数据
        instance.save()
  • 小结
    • 序列化类中重写 update 方法
    • 在后端逻辑中调用 save 方法
    • 需要传入的参数 ser = BookSerializer(instance=book,data=request.data)

【七】save方法简解

  • 在视图类中,无论是保存还是修改,都是调用序列化类的 save() 方法

  • 底层实现逻辑是根据 instance 判断需要做出的对应的方法

    def save(self, **kwargs):
		
        # 根据传入的 instance 对象,进行方式的判断 update/create
        # 如果是 update 方法 需要传入需要需要修改的独享和修改的数据
        if self.instance is not None:
            self.instance = self.update(self.instance, validated_data)
            assert self.instance is not None, (
                '`update()` did not return an object instance.'
            )
        else:
            # 如果是 create 需要传入修改的数据
            self.instance = self.create(validated_data)
            assert self.instance is not None, (
                '`create()` did not return an object instance.'
            )

        return self.instance
  • 如果是 update 方法 需要传入需要需要修改的独享和修改的数据

  • 如果是 create 需要传入修改的数据

【作业】

Publish 的5个接口  
-name
-addr
-phone

出版社名字不能包含东京,最大长度不能超过10,最短不能短于2
地址长度不能超过20,没有最短现在,地址不能以上海开头
手机号必须11位
出版社名字和地址名字不能一样
  • 路由
path('publish/', PublishView.as_view()),
path('publish/<int:pk>/', PublishDetailView.as_view()),
path('publish/add/', PublishDetailView.as_view()),
  • 视图
class PublishView(APIView):
    # 查询所有信息
    def get(self, request):
        publish_obj = models.Publish.objects.all()
        publish_ser = PublishSerializer(instance=publish_obj, many=True)

        data = publish_ser.data

        return Response({"code": 200, "msg": "查询成功", "data": data})


class PublishDetailView(APIView):
    # 查询指定信息
    def get(self, request, pk):
        publish_obj = models.Publish.objects.filter(pk=pk).first()
        if publish_obj:
            publish_ser = PublishSerializer(instance=publish_obj)
            data = publish_ser.data

            return Response({"code": 200, "msg": "查询成功", "data": data})
        else:
            return Response({"code": 201, "msg": "查询失败,书籍不存在", "data": []})

    # 新增
    def post(self, request):
        publish_ser = PublishSerializer(data=request.data)
        if publish_ser.is_valid():
            publish_ser.save()
            after_data = publish_ser.validated_data
            print(after_data)
            return Response({"code": 200, "msg": "保存成功", "data": []})
        else:
            return Response({"code": 201, "msg": "保存失败", "error": publish_ser.errors})
  • 序列化类
class PublishSerializer(serializers.Serializer):
    id = serializers.IntegerField(required=False)
    name = serializers.CharField(max_length=12, min_length=4)
    addr = serializers.CharField(max_length=18, error_messages={"errors": "地址最大长度不能超过18"})
    phone = serializers.IntegerField(error_messages={"errors": "手机号必须是11位"})

    def create(self, validated_data):
        publish = models.Publish.objects.create(**validated_data)
        return publish

    def update(self, instance, validated_data):
        for item in validated_data:
            setattr(instance, item, validated_data[item])
        instance.save()
        return instance

    def validate_name(self, name):
        # 出版社名字不能包含东京
        if "东京" in name:
            raise ValidationError("地址中不能包含敏感字 '东京' !")
        else:
            return name

    def validate_addr(self, addr):
        # 地址不能以上海开头
        if addr.startswith("上海"):

            raise ValidationError("地址中不能以上海开头 !")
        else:
            return addr

    def validate_phone(self, phone):
        if len(str(phone)) != 11:
            raise ValidationError("电话必须是11位")
        else:
            return phone

    def validate(self, attrs):
        # 出版社名字和地址名字不能一样

        name = attrs.get('name')
        addr = attrs.get('addr')

        if name == addr:
            raise ValueError("姓名和地址不能相等!")
        else:
            return attrs
  • 模型表
class Publish(models.Model):
    name = models.CharField(max_length=32)
    addr = models.CharField(max_length=255)
    phone = models.IntegerField()

【补充】

from django.db.models import QuerySet
from rest_framework.response import Response
from rest_framework.views import APIView
from .models import User, Book, Publish
from .serializers import UserSerializer, BookSerializer, PublishSerializer
from rest_framework.generics import GenericAPIView

###################################方式一:基于APIView封装方法#####################################


# 构建自定义返回对象
def CommonResponse(code=100, msg="请求成功", status=None, **kwargs):
    # 构建返回格式字典:默认是 {'code': 100, 'msg': "请求成功"}
    data = {'code': code, 'msg': msg}

    # 如果有数据传进来进行数据的更新
    if kwargs:
        for name, value in kwargs.items():
            data[name] = value
    # 完整的数据格式示例 : {'code': 100, 'msg': "请求成功", 'data': {....}}
    return Response(data=data, status=status)


# 构建 视图基类
class GenericView(APIView):

    # 定义序列化类校验方法
    def _check_serializers(self, serializer_class, instance=None, many=False, data=None, **kwargs):
        # 没有数据 data -----> 序列化返回数据
        if not data:
            if many:
                # many=True -----> 序列化返回多条数据
                return serializer_class(instance=instance, many=many, **kwargs)
            else:
                # many=False -----> 序列化返回单条数据
                return serializer_class(instance=instance, **kwargs)
        # 有数据  data -----> 反序列化保存数据
        return serializer_class(instance=instance, data=data, **kwargs)

    # 定义中转调用序列化器
    def get_serializer_class(self, serializer_class=None, instance=None, many=False, data=None, **kwargs):
        # serializer_class : 视图需要用到的序列化类
        # instance : 视图传过来的 对象 (.all()/.first())
        # many : 是否序列化多条
        # data : 反序列化的数据
        return self._check_serializers(serializer_class=serializer_class, instance=instance, many=many,
                                       data=data, **kwargs)

    # 定义校验视图函数 ---> 返回指定的表模型和序列化器
    def _check_view(self, request):
        # 根据 request 对象获取当前视图类
        view_name = request.resolver_match.view_name
        view_name = view_name.split('.')[-1]
        # 如果属于指定视图类则返回指定 模型表和序列化类
        if view_name.split('.')[-1] in ['UserView', 'UserDetailView']:
            model_name = User
            serializer_class = UserSerializer
        elif view_name.split('.')[-1] in ['BookView', 'BookDetailView']:
            model_name = Book
            serializer_class = BookSerializer
        else:
            model_name = Publish
            serializer_class = PublishSerializer
        # 将 模型表和序列化类 返回
        return model_name, serializer_class

    # 定义 获取 queryset 的方法
    def get_queryset(self, model_name, pk=None):
        # 带 PK 返回 指定对象
        if pk:
            return model_name.objects.filter(pk=pk).first()
        # 不带 PK 返回 全部对象
        return model_name.objects.all()

    # 定义基类 get 方法 --- 查询
    def _get(self, request, pk=None, *args, **kwargs):
        # 判断是查询单条还是全部
        if pk:
            # 获取 模型表和序列化类
            model_name, serializer_class = self._check_view(request)
            # 获取 queryset 对象 ---> 指定
            queryset = self.get_queryset(model_name, pk)
            # 调用序列化类序列化数据
            serializer = self.get_serializer_class(serializer_class=serializer_class, instance=queryset)
            # 返回自定义 数据
            return CommonResponse(data=serializer.data)
        else:
            # 获取 模型表和序列化类
            model_name, serializer_class = self._check_view(request)
            # 获取 queryset 对象 ---> 全部
            queryset = self.get_queryset(model_name)
            # 调用序列化类序列化数据
            serializer = self.get_serializer_class(serializer_class=serializer_class, instance=queryset,
                                                   many=True)
            # 返回自定义 数据
            return CommonResponse(data=serializer.data)

    def _post(self, request, *args, **kwargs):
        model_name, serializer_class = self._check_view(request)
        serializer = self.get_serializer_class(serializer_class=serializer_class, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return CommonResponse(msg="创建成功")
        else:
            return CommonResponse(error=serializer.errors)

    # 定义基类 put 方法 --- 更新
    def _put(self, request, pk, *args, **kwargs):
        # 获取 模型表和序列化类
        model_name, serializer_class = self._check_view(request)
        # 获取 queryset 对象 ---> 指定
        queryset = self.get_queryset(model_name, pk)
        # 调用序列化类序列化数据
        serializer = self.get_serializer_class(serializer_class=serializer_class, instance=queryset,
                                               data=request.data)
        # 调用序列化类校验数据
        if serializer.is_valid():
            # 保存数据 ---- 触发 update 方法
            serializer.save()
            # 返回自定义 数据
            return CommonResponse(msg="数据更新成功")
        else:
            # 返回自定义 数据 --- 错误
            return CommonResponse(error=serializer.errors)

    def _delete(self, request, pk, *args, **kwargs):
        # 获取 模型表和序列化类
        model_name, serializer_class = self._check_view(request)
        # 获取 queryset 对象 ---> 指定
        queryset = self.get_queryset(model_name, pk)
        # 直接删除对象
        queryset.delete()
        # 返回自定义 数据
        return CommonResponse(msg="删除数据成功")


class UserView(GenericView):

    # 获取全部信息
    def get(self, request, *args, **kwargs):
        return self._get(request, *args, **kwargs)

    # 创建信息
    def post(self, request, *args, **kwargs):
        return self._post(request, *args, **kwargs)


class UserDetailView(GenericView):
    # 获取单条信息
    def get(self, request, pk, *args, **kwargs):
        return self._get(request, pk, *args, **kwargs)

    # 更新信息
    def put(self, request, pk, *args, **kwargs):
        return self._put(request, pk, *args, **kwargs)

    # 删除信息
    def delete(self, request, pk, *args, **kwargs):
        return self._delete(request, pk, *args, **kwargs)


class BookView(GenericView):
    # 获取全部信息
    def get(self, request, *args, **kwargs):
        return self._get(request, *args, **kwargs)

    # 创建信息
    def post(self, request, *args, **kwargs):
        return self._post(request, *args, **kwargs)


class BookDetailView(GenericView):
    # 获取单条信息
    def get(self, request, pk, *args, **kwargs):
        return self._get(request, pk, *args, **kwargs)

    # 更新信息
    def put(self, request, pk, *args, **kwargs):
        return self._put(request, pk, *args, **kwargs)

    # 删除信息
    def delete(self, request, pk, *args, **kwargs):
        return self._delete(request, pk, *args, **kwargs)


class PublishView(GenericView):
    # 获取全部信息
    def get(self, request, *args, **kwargs):
        return self._get(request, *args, **kwargs)

    # 创建信息
    def post(self, request, *args, **kwargs):
        return self._post(request, *args, **kwargs)


class PublishDetailView(GenericView):
    # 获取单条信息
    def get(self, request, pk, *args, **kwargs):
        return self._get(request, pk, *args, **kwargs)

    # 更新信息
    def put(self, request, pk, *args, **kwargs):
        return self._put(request, pk, *args, **kwargs)

    # 删除信息
    def delete(self, request, pk, *args, **kwargs):
        return self._delete(request, pk, *args, **kwargs)


##############################方法二:基于GenericAPIView封装方法##################################
'''

from rest_framework import generics
from rest_framework.response import Response
from .models import User, Book, Publish
from .serializers import UserSerializer, BookSerializer, PublishSerializer


# 构建自定义返回对象
def CommonResponse(code=100, msg="请求成功", status=None, **kwargs):
    data = {'code': code, 'msg': msg}
    if kwargs:
        for name, value in kwargs.items():
            data[name] = value
    return Response(data=data, status=status)


# 构建基础视图类
class BaseView(generics.GenericAPIView):
    queryset = None
    serializer_class = None

    def get_queryset(self):
        return self.queryset.all()

    def get_serializer(self, *args, **kwargs):
        return self.serializer_class(*args, **kwargs)

    def get_object(self, pk):
        return generics.get_object_or_404(self.get_queryset(), pk=pk)

    def get(self, request, pk=None):
        if pk:
            instance = self.get_object(pk)
            serializer = self.get_serializer(instance)
        else:
            queryset = self.get_queryset()
            serializer = self.get_serializer(queryset, many=True)
        return CommonResponse(data=serializer.data)

    def post(self, request):
        serializer = self.get_serializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return CommonResponse(msg="创建成功")
        else:
            return CommonResponse(error=serializer.errors)

    def put(self, request, pk):
        instance = self.get_object(pk)
        serializer = self.get_serializer(instance, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return CommonResponse(msg="数据更新成功")
        else:
            return CommonResponse(error=serializer.errors)

    def delete(self, request, pk):
        instance = self.get_object(pk)
        instance.delete()
        return CommonResponse(msg="删除数据成功")


# 用户视图类
class UserView(BaseView):
    queryset = User.objects.all()
    serializer_class = UserSerializer


class UserDetailView(BaseView):
    queryset = User.objects.all()
    serializer_class = UserSerializer


# 图书视图类
class BookView(BaseView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer


class BookDetailView(BaseView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer


# 出版社视图类
class PublishView(BaseView):
    queryset = Publish.objects.all()
    serializer_class = PublishSerializer


class PublishDetailView(BaseView):
    queryset = Publish.objects.all()
    serializer_class = PublishSerializer


'''

【补充】IntegerField BigIntegerField 的区别:

IntegerField和BigIntegerField是Django框架中用于存储整数值的两种字段类型,它们之间有以下区别:

  1. 存储范围:

    • IntegerField:存储范围是一个32位有符号整数,即介于-2,147,483,648到+2,147,483,647之间的整数。
    • BigIntegerField:存储范围是一个64位有符号整数,即介于-9,223,372,036,854,775,808到+9,223,372,036,854,775,807之间的整数。
  2. 存储空间:

    • IntegerField:使用4字节内存来存储整数值。
    • BigIntegerField:使用8字节内存来存储整数值。
  3. 应用场景:

    • IntegerField:适用于较小的整数值,如用户ID、年龄等。
    • BigIntegerField:适用于较大的整数值,如订单号、数据库记录ID等。

需要注意的是,在大多数情况下,IntegerField已经足够满足应用需求。只有当需要存储非常大的整数值时,才需要使用BigIntegerField。

posted @ 2023-07-31 12:19  Chimengmeng  阅读(36)  评论(0编辑  收藏  举报