bookserlizer 十大接口

Posted zhm-cyt

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bookserlizer 十大接口相关的知识,希望对你有一定的参考价值。

目录

一、导包补充
二、深度查询

  1. 子序列化和depth方法
    (1)什么是子序列化
    (2)all 方法 exclude方法
    (3)子序列化实例
  2. 插拔式
    三、十大接口
  3. 十大接口中注意点(很重要)
  4. 实例
    一、导包补充
    在导包时,如果用from 文件1 import ,则文件1中的以下划线开头的变量,都不能访问。无论是单下划线还是双下线。
    用import 文件1的方式,还是能访问到文件1中以下划线开头的变量,没有影响。
    当我们用from 文件1 import
    的方式导包时,怎么才能访问到其中以下划线开头的变量呢,解决方法如下

    解决方法: 使用 __all__方法将下划线开头的变量储存。

    __all__本来就默认包含普通的变量,所以自己重写__all__时,要把普通变量也加上

例子

文件1中:

x = 10
_y = 20
__z = 30

all = [‘x‘,‘_y‘,‘__z‘]

文件2中

from 文件1 import *

print(x, _y, __z) # 结果: 10 20 30
二、深度查询
深度查询主要是用在:当给前端数据时,要求带有与当前表有关联的其他表的某些数据。通过连表操作,将关联表中的数据一起序列化
外键字段默认显示的是外键值(int类型),不会自己进行深度查询,但配置了深度查询就可以实现将关联的那一个表的字段序列化

深度查询共有3中方式:

子序列化:必须有子序列化类配合,不能反序列化了
depth方法:自动深度查询的是关联表的所有字段,数据量太多
插拔式:@property,方法名字不能与外键名同名

  1. 子序列化和depth方法
    (1)什么是子序列化
    当前序列化类1,与上面的其他序列化类2,通过外键字段相关联,这样当前序列化类序列化时,就会将序列化类2中的配置的序列化字段一同序列化
    进行子序列化,关联变量必须为连表时的 正反向外键字段
    子序列化和depth方法都不常用
    (2)all 方法 exclude方法
    用法见下面的实例
    但__all__ 方法和exclude方法不能同时使用
    (3)子序列化实例

序列化类2

class BookModelSerializer(serializers.ModelSerializer):

class Meta:
    model = models.Book
    fields = ['name', 'price', 'publish', 'authors', 'publish_info', 'author_list']
    extra_kwargs = {
        'publish': {
            'write_only': True
        },
        'authors': {
            'write_only': True
        }
    }

序列化类1

class PublishModelSerializer(serializers.ModelSerializer):
# 子序列化:
# 1)只能在序列化中使用
# 2)字段名必须是外键(正向反向)字段
# 因为自定义序列化字段是默认read_only,是不能参与反序列化的,而子序列化必须为外键名,所以就无法入库
# 3)在外键关联数据记录是多条时,需要明确many=True
# 4)是单向操作,因为作为子系列的类必须写在上方,所以不能产生逆方向的子序列化
books = BookModelSerializer(many=True) # 子序列化关联字段
class Meta:
model = models.Publish
fields = [‘name‘, ‘address‘, ‘books‘]

    # 了解配置
    # fields = '__all__'  # 表示当前模型表的所有字段都参与序列化和反序列化,但这种方法我们不能配置extra_kwargs了。外键字段默认显示的是外键值(int类型),不会自己进行深度查询,但配置了子序列化就可以实现了
    
    # exclude = ['name']  # 表示出了name字段,该模型表的其他所有字段都参与序列化和反序列化
    
    # depth = 2  # 自动深度,值代表深度次数 如publis表序列化了外键字段books,没有使用子序列化时,则对应的book数据都会被序列化,这是深度1,若book表有外键字段,也被序列化了,则也会将book表对应另一个的表数据序列化,这是深度2。深度2包含深度1,就是深度值越大,能序列化的关联表层次越多
  1. 插拔式
    名字不能与外键名同名

用哪些字段,就在模型类中定义哪些函数,其返回值就是要序列化的字段的值

实例

models文件中**********************************

class Book(BaseModel):
name = models.CharField(max_length=64)
price = models.DecimalField(max_digits=10, decimal_places=2)
publish = models.ForeignKey(to=‘Publish‘, related_name=‘books‘, db_constraint=False, on_delete=models.DO_NOTHING, null=True)
authors = models.ManyToManyField(to=‘Author‘, related_name=‘books‘, db_constraint=False)

# 自定义插拔式深度查询字段
@property
def publish_info(self):  # 单个数据
    # from .serializers import PublishModelSerializer
    # return PublishModelSerializer(self.publish).data
    return {
        'name': self.publish.name,
        'address': self.publish.address,
    }

# 自定义插拔式深度查询字段
@property
def author_list(self):
    author_list_temp = []  # 存放所有作者格式化成数据的列表
    authors = self.authors.all()  # 所有作者
    for author in authors:  # 遍历处理所有作者
        author_dic = {
            'name': author.name,
        }
        try:  # 有详情才处理详情信息
            author_dic['mobile'] = author.detail.mobile
        except:
            author_dic['mobile'] = '无'

        author_list_temp.append(author_dic)  # 将处理过的数据添加到数据列表中

    return author_list_temp  # 返回处理后的结果



def __str__(self):
    return self.name

自定义的序列化文件中:*******************************

class BookModelSerializer(serializers.ModelSerializer):

class Meta:
    model = models.Book
    fields = ['name', 'price', 'publish', 'authors', 'publish_info', 'author_list']
    extra_kwargs = {
        'publish': {
            'write_only': True
        },
        'authors': {
            'write_only': True
        }
    }    

三、十大接口

  1. 十大接口中注意点(很重要)
    就是get/post/put/patch/delete对应的单操作和群操作,一共对应数据的10种操作。实际的每种资源(book,publish,user等)对应的url各只有两条。
    5大请求方式对应的注意点
    最重要的是,所有群操作中只要有一个数据提供的有误,整个群操作都不会成功。
    下面写的many=True,partial=True都是给序列化类传的关键字参数。partial=True参数的作用是让反序列化的所有字段都变为可选字段(即required=False)。还有一个参数context,它的作用是给序列化类传参。在序列化类中,序列化对象.context 就可以访问到了。
    序列化时要传对象,反序列化时传的是data=request.data(传的是前端提交的数据)。序列化类中包含序列化操作和反序列化操作。
    代码书写中的细节
    1)get:单查群查 => 群查时要有 many=True
    2)post:单增群增 => 群增时要有 many=True
    3)delete:单删群删 => 只做表中is_delete字段的修改,不需要序列化类的参与
    4)put/patch:单改群改 =>

群改时内部其实是用默认的ListSerializer序列化类对多个数据记录进行遍历,再使用ModelSerializer提供的单改的方法轮流修改每个数据记录。(其实ModelSerializer序列化成员内部为我们写好了单增和群增方法,也写好了单改的方法。只有群增没写,需要我们重写update方法。所以群改时:要在相应的序列化类中自定义配置list_serializer_class = 自定义的ListSerializer类,变量名必须是list_serializer_class。在自定义的ListSerializer类中重写update方法,重写的内容就是把群改的对象一个个传入原单改的方法中)

patch方法中要有partial=True,表示反序列化的字段都变成选填字段,提供就修改成提供的,不提供就使用原来的。 其实整体改和局部改的方法差不多,局部改更加简便合理,以后对数据的修改都用patch方法就可以了

5)群操作中,在序列化类中都要传入many=True这个参数。
在群增时,通过many=True来分别,内部是直接新增还是遍历分别新增

在整体改和局部改中,通过many=True和partial=True这两个参数,来区分是整体改还是局部改,通过many=True来执行响应的操作。当many=True时,还需要手动再序列化类中重写群改方法。

6)在进行数据的修改时,实例化序列化对象时,有一个参数为:instance= 要修改的数据对象 。当instance参数有值时,表示下面的save方法进行的是修改操作,当instance参数没值时,表示下面的save方法进行的是新增操作。instance默认是None

7)序列化对象.is_valid(raise_exception=True) # 表示校验是否全部通过,通过这继续往下走,有一个没通过就停止,自动返回异常信息给前端

  1. 实例

    views文件中:***************************************

from rest_framework.views import APIView
from rest_framework.response import Response
from . import models, serializers
from .response import APIResponse

出版社群查

class PublishAPIView(APIView):
def get(self, request, *args, **kwargs):
publish_query = models.Publish.objects.all()
publish_ser = serializers.PublishModelSerializer(publish_query, many=True)
return APIResponse(results=publish_ser.data)

class BookAPIView(APIView):
# 单查群查
def get(self, request, *args, **kwargs):
pk = kwargs.get(‘pk‘)
if pk:
book_obj = models.Book.objects.filter(is_delete=False, pk=pk).first()
book_ser = serializers.BookModelSerializer(book_obj)
else:
book_query = models.Book.objects.filter(is_delete=False).all()
book_ser = serializers.BookModelSerializer(book_query, many=True)
return APIResponse(results=book_ser.data)
# return Response(data=book_ser.data)

# 单删群删
def delete(self, request, *args, **kwargs):
    """
    单删:接口:/books/(pk)/   数据:空
    群删:接口:/books/   数据:[pk1, ..., pkn]
    逻辑:修改is_delete字段,修改成功代表删除成功,修改失败代表删除失败
    """
    pk = kwargs.get('pk')
    if pk:
        pks = [pk]  # 将单删格式化成群删一条
    else:
        pks = request.data  # 群删
    try:  # 数据如果有误,数据库执行会出错
        rows = models.Book.objects.filter(is_delete=False, pk__in=pks).update(is_delete=True)
    except:
        return APIResponse(1, '数据有误')

    if rows:
        return APIResponse(0, '删除成功')
    return APIResponse(1, '删除失败')

# 单增群增
def post(self, request, *args, **kwargs):
    """
    单增:接口:/books/   数据:{...}
    群增:接口:/books/   数据:[{...}, ..., {...}]
    逻辑:将数据给系列化类处理,数据的类型关系到 many 属性是否为True
    """
    if isinstance(request.data, dict):
        many = False
    elif isinstance(request.data, list):
        many = True
    else:
        return Response(data={'detail': '数据有误'}, status=400)

    book_ser = serializers.BookModelSerializer(data=request.data, many=many)
    book_ser.is_valid(raise_exception=True)
    book_obj_or_list = book_ser.save()
    return APIResponse(results=serializers.BookModelSerializer(book_obj_or_list, many=many).data)


# 整体单改群改
def put(self, request, *args, **kwargs):
    """
    单改:接口:/books/(pk)/   数据:{...}
    群增:接口:/books/   数据:[{pk, ...}, ..., {pk, ...}]
    逻辑:将数据给系列化类处理,数据的类型关系到 many 属性是否为True
    """
    pk = kwargs.get('pk')
    if pk:  # 单改
        try:
            # 与增的区别在于,需要明确被修改的对象,交给序列化类
            book_instance = models.Book.objects.get(is_delete=False, pk=pk)
        except:
            return Response({'detail': 'pk error'}, status=400)

        book_ser = serializers.BookModelSerializer(instance=book_instance, data=request.data)
        book_ser.is_valid(raise_exception=True)
        book_obj = book_ser.save()
        return APIResponse(results=serializers.BookModelSerializer(book_obj).data)
    else:  # 群改
        # 分析(重点):
        # 1)数据是列表套字典,每个字典必须带pk,就是指定要修改的对象,如果有一条没带pk,整个数据有误
        # 2)如果pk对应的对象已被删除,或是对应的对象不存在,可以认为整个数据有误(建议),可以认为将这些错误数据抛出即可
        request_data = request.data
        try:
            pks = []
            for dic in request_data:
                pk = dic.pop('pk')  # 解决分析1,没有pk,pop方法就会抛异常
                pks.append(pk)

            book_query = models.Book.objects.filter(is_delete=False, pk__in=pks).all()
            if len(pks) != len(book_query):
                raise Exception('pk对应的数据不存在')
        except Exception as e:
            return Response({'detail': '%s' % e}, status=400)

        book_ser = serializers.BookModelSerializer(instance=book_query, data=request_data, many=True)
        book_ser.is_valid(raise_exception=True)
        book_list = book_ser.save()
        return APIResponse(results=serializers.BookModelSerializer(book_list, many=True).data)



# 局部单改群改
def patch(self, request, *args, **kwargs):
    pk = kwargs.get('pk')
    if pk:  # 单改
        try:
            book_instance = models.Book.objects.get(is_delete=False, pk=pk)
        except:
            return Response({'detail': 'pk error'}, status=400)
        # 设置partial=True的序列化类,参与反序列化的字段,都会置为选填字段
        # 1)提供了值得字段发生修改。
        # 2)没有提供的字段采用被修改对象原来的值

        # 设置context的值,目的:在序列化完成自定义校验(局部与全局钩子)时,可能需要视图类中的变量,如请求对象request
        # 可以通过context将其传入,在序列化校验方法中,self.context就能拿到传入的视图类中的变量
        book_ser = serializers.BookModelSerializer(instance=book_instance, data=request.data, partial=True, context={'request': request})
        book_ser.is_valid(raise_exception=True)
        book_obj = book_ser.save()
        return APIResponse(results=serializers.BookModelSerializer(book_obj).data)
    else:  # 群改
        request_data = request.data
        try:
            pks = []
            for dic in request_data:
                pk = dic.pop('pk')
                pks.append(pk)

            book_query = models.Book.objects.filter(is_delete=False, pk__in=pks).all()
            if len(pks) != len(book_query):
                raise Exception('pk对应的数据不存在')
        except Exception as e:
            return Response({'detail': '%s' % e}, status=400)

        book_ser = serializers.BookModelSerializer(instance=book_query, data=request_data, many=True, partial=True)
        book_ser.is_valid(raise_exception=True)
        book_list = book_ser.save()
        return APIResponse(results=serializers.BookModelSerializer(book_list, many=True).data)

自定义的序列化模块文件中:*********************************

from rest_framework import serializers
from . import models

多表操作

class BookListSerializer(serializers.ListSerializer):
# 自定义的群增群改辅助类,没有必要重写create方法
def create(self, validated_data):
return super().create(validated_data)

def update(self, instance_list, validated_data_list):
    return [
        self.child.update(instance_list[index], attrs) for index, attrs in enumerate(validated_data_list)
    ]

class BookModelSerializer(serializers.ModelSerializer):
# 外键字段默认显示的是外键值(int类型),不会自己进行深度查询
# 深度查询方式:
# 1)子序列化:必须有子序列化类配合,不能反序列化了
# 2)配置depth:自动深度查询的是关联表的所有字段,数据量太多
# 3)插拔式@property:名字不能与外键名同名
class Meta:
# ModelSerializer默认配置了ListSerializer辅助类,帮助完成群增群改
# list_serializer_class = serializers.ListSerializer
# 如果只有群增,是不需要自定义配置的,但要完成群改,必须自定义配置
list_serializer_class = BookListSerializer

    model = models.Book
    fields = ['name', 'price', 'publish', 'authors', 'publish_info', 'author_list']
    extra_kwargs = {
        'publish': {
            'write_only': True
        },
        'authors': {
            'write_only': True
        }
    }

# 验证视图类是否将request请求参数通过context传入
def validate(self, attrs):
    print('传入的request: %s' % self.context.get('request'))
    return attrs

class PublishModelSerializer(serializers.ModelSerializer):
# 子序列化:
# 1)只能在序列化中使用
# 2)字段名必须是外键(正向反向)字段
# 因为相对于自定义序列化外键字段,自定义序列化字段是不能参与反序列化的,而子序列化必须为外键名,所以就无法入库
# 3)在外键关联数据是多条时,需要明确many=True
# 4)是单向操作,因为作为子系列的类必须写在上方,所以不能产生逆方向的子序列化
# books = BookModelSerializer(many=True)
class Meta:
model = models.Publish
fields = [‘name‘, ‘address‘, ‘books‘]

models文件中:*************************************

from django.db import models

Book表:

Publish表:

Author表:

AuthorDetail表:

from django.contrib.auth.models import User
class BaseModel(models.Model):
is_delete = models.BooleanField(default=False)
created_time = models.DateTimeField(auto_now_add=True)

class Meta:
    # 基表,为抽象表,是专门用来被继承,提供公有字段的,自身不会完成数据库迁移
    abstract = True

class Book(BaseModel):
name = models.CharField(max_length=64)
price = models.DecimalField(max_digits=10, decimal_places=2)
publish = models.ForeignKey(to=‘Publish‘, related_name=‘books‘, db_constraint=False, on_delete=models.DO_NOTHING, null=True)
authors = models.ManyToManyField(to=‘Author‘, related_name=‘books‘, db_constraint=False)

# 自定义反序列化的外键字段,不能与真的外键字段重名
@property
def publish_info(self):  # 单个数据
    
    return {
        'name': self.publish.name,
        'address': self.publish.address,
    }

# 自定义反序列化的外键字段,不能与真的外键字段重名
@property
def author_list(self):
    author_list_temp = []  # 存放所有作者格式化成数据的列表
    authors = self.authors.all()  # 所有作者
    for author in authors:  # 遍历处理所有作者
        author_dic = {
            'name': author.name,
        }
        try:  # 有详情才处理详情信息
            author_dic['mobile'] = author.detail.mobile
        except:
            author_dic['mobile'] = '无'

        author_list_temp.append(author_dic)  # 将处理过的数据添加到数据列表中

    return author_list_temp  # 返回处理后的结果



def __str__(self):
    return self.name

class Publish(BaseModel):
name = models.CharField(max_length=64)
address = models.CharField(max_length=64)

class Author(BaseModel):
name = models.CharField(max_length=64)

class AuthorDetail(BaseModel):
mobile = models.CharField(max_length=64)
author = models.OneToOneField(to=Author, related_name=‘detail‘, db_constraint=False, on_delete=models.CASCADE)

以上是关于bookserlizer 十大接口的主要内容,如果未能解决你的问题,请参考以下文章

JAVA8 十大新特性详解

JAVA8 十大新特性详解

十大视频场景化应用工具+五大视频领域冠军/顶会算法开源

阿里巴巴和四十大盗英文原文

jdk8十大特性并代码demo(转)

十大视频场景化应用工具+五大视频领域冠军顶会算法重磅开源!