(day74)深度查询(子序列化、depth、@property)、二次封装Response、十大接口

一、外键连表深度查询

(一)子序列化

  1. 必须有子序列化类配合,不能参与反序列化
  2. 字段名必须是外键字段,正向反向均可
    • 自定义序列化字段是不能参与反序列化,而子序列化必须是自定义序列化字段(外键名),所以就无法入库
  3. 外键关联数据是多条时,需要明确many=True
  4. 子序列的类必须写在上方,是一种单向操作
class BookModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Book
        fields = '__all__'

class PublishModelSerializer(serializers.ModelSerializer):
    books = BookModelSerializer(many=True)
    class Meta:
        model = models.Publish
        # fields = '__all__'
        fields = ['name', 'address', 'books']

(二)depth配置

  1. 自动深度查询,值代表深度次数
  2. 被深度的外键采用__all__,因此会显示所有字段值,无法自定义配置
  • exclude属性:除了该字段
  • __all__:除了_开头的以外的所有字段
class PublishModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Publish
        fields = ['name', 'address', 'books']

        # 了解配置
        # fields = '__all__'  # 包含所有,不包括_开头的变量,
        # exclude = ['name']  # 除了name字段都会被显示 
        depth = 2  # 自动深度,值代表深度次数,但是被深度的外键采用__all__,显示所以字段

(三)@property自定义序列字段

  1. 名字不能与外键名同名
  2. 返回单个数据时,可以通过序列化类完成序列化,也可以手动序列化
  3. 返回多个数据时,通过遍历添加完成序列化
    • 注意:要考虑到关联的表中没有相应数据的情况(断开关联时),可以通过try进行错误捕捉
# models.py
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)
    
    # 1. 一对多:单个数据(两种方式序列化)
    @property
    def publish_info(self):
        # from .serializers import PublishModelSerializer
        # return PublishModelSerializer(self.publish).data 
        return {
            'name':self.publish.name,
            'address':self.publish.address
        }
    
    # 2. 多对多:多个数据
    @property
    def author_list(self):
        author_list_temp=[]  # 存放所有作者格式化成数据的列表
        authors = self.authors.all()  # 所有作者
        for author in authors:  # 遍历处理所有作者
            author_dic = {
                'name':author.name,
            }
            
            # 防止出现作者没有对应详细信息(电话)的情况,因此用try捕捉
            try:
                author_dic['mobile'] = author.detail.mobile
            except:
                author_dic['mobile'] = '无'
               
            author_list_temp.append(author_dic)  # 把处理后的数据添加到数据列表中
           
        return author_list_temp

二、二次封装Response

  1. 对drf的响应模块进行二次封装,减少代码的重复,使代码更加精简
  2. 通过新建response.py文件,在文件内将返回Response进行二次封装成一个类(面对对象封装)

  3. 要注意results不为None和results不为空是两个概念

# response.py
from rest_framework.response import Response
class APIResponse(Response):
    def __init__(self,status=0,msg='ok',results=None,http_status=None,headers=None,exception=False,content_type=None,**kwargs):
        # 将status、msg、results、kwargs、格式化成data
        data = {
            'status':status,
            'msg':msg,
        }
        # 2. results只要不为空就都是数据,比如False、0、''都是数据,因此判断条件不能写 if results
        
        if results is not None:
            data['results'] = results
           
        data.update(**kwargs)
        
        super().__init__(data=data,status=http_status,headers=headers,exception=exception,content_type=content_type)

三、十大接口

  • 一个视图、两个url,十个接口
    1. 视图类BookAPIView
    2. 两个url:
      • url(r'^books/$', views.BookAPIView.as_view()),
      • `url(r'^books/(?P<pk>\d+)/$', views.BookAPIView.as_view()),
    3. 十大接口(五大请求):get、post、put、patch、delete

(一)单查群查(get)

  • 二次封装Response的使用
from . import models,serializers
from response import APIResponse
class BookAPIView(APIView):
    # 单查群查
    def get(self,request,*args,**kwargs):
        pk = kwargs.get('pk')
        if pk:
            # 单查
            book_obj = models.Book.object.filter(is_delete=False,pk=pk).first()
            book_ser = serializers.BookModeSerialier(book_obj)
        else:
            # 多查
            book_query = models.Book.object.filter(is_delete=False).all()
            book_ser = serializers.BookModelSerialzer(book_query,many=True)
           
        return APIResponse(results=book_ser.data)
        '''
        return Reponse(data={
            'status':0,
            'msg':'pk'
            'results':book_ser.data
        })
        '''

(二)单删群删(delete)

  1. 逻辑:修改is_delete字段值,修改成功代表删除成功
  2. 单删群删:只要拼接参数中有值则为单删,否则为群删
  3. 删除操作时,要考虑到用户不存在、群删部分成功等情况,一律视为客户端请求数据有误
  4. 群删时,可以通过神奇的下划线方法pk__in进行操作
def delete(self,request,*args,**kwargs):
    pk = kwargs.get('pk')
    if pk:
        pks = pk
    else:
        pks = request.data
        
    # 3. 数据如果有误,数据库执行会出错    
    try:    
        rows = models.Book.object   .filter(is_delete=False,pk__in=pks).update(is_dalete=True)
    except:
        return APIRsponse(1,'数据有误')
    if rows:
        return APIResponse(0,'删除成功')
    return APIResponse(1,'删除失败')

(三)单增群增(post)

  1. 单增和群增都是同一个url(/books/),通过判断数据包参数是否是字典还是列表来确定是单增还是群增
  2. serrialzier对象的is_valid方法中的raise_exception属性为True时,如果脚丫内部通过,会自动返回错误对象给前端
def post(self,request,*args,**kwargs):
    # 1. 判断数据包参数是字典还是列表来确定是单增还是群增
    if instance(request.data,dict):
        many = False
    elif instance(request,data,list):
        many = True
    else:
        return Response(data={'detail': '数据有误'}, status=400)
    
    book_ser = serializers.BookModelSerilazer(data=request.data,many=many)
    book_ser.is_valid(raise_exception = True)  # 2. 通过自动返回错误对象
    book_obj_or_list = book_ser.save()
    return APIResponse(results=serializer,BookModelSerializer(book_obj_or_list,many=many).data)                                   

(四)整体单改群改(put)

  • 单改
    1. 与单增群增的区别在于:序列化数据时,是否有表数据对象instance
      • 后台会检测如果有instance,则调用updata方法
      • 如果没有instance,则调用create方法
    2. ModelSerializer类中只定义了create方法,没有定义update方法,因此update方法要手动定义
    3. 通过捕捉异常来实现,如果表数据对象不存在,返回给前端错误对象
  • 群改
    1. 通过列表套字典的数据传递,当某个字典中没有pk值,或者对应的表数据对象已被删除,或者不存在时,可以认为整个数据有误,直接抛出即可
def put(self,request,*args,**kwargs):
    pk = kwargs.get('pk')
    if pk:  # 单改
        try:  # 3. 通过捕捉异常来实现,如果表数据对象不存在,返回给前端错误对象
            book_instance = models.Book.object.get(is_delete=False,pk=pk)
           
        except:
            return Response({'detail':'pk error'},status=400)
        
        # 1. 通过传入的instance是否为空,后台判断调用update还是create方法 
        book_ser = serializer.BookModelSerializer(instance=book_instance,data=request.data)
        book_ser.is_valid(raise_exception=True)
        book_obj = book_ser.save()
        return APIResponse(results=serializer.BookModelSerialzier(book_obj).data)
    else:  # 群改
        try:
            pks=[]
            for dic in request.data:
                pk = dic.pop('pk')  # 4.1 没有pk就会被捕捉异常
                pks.append(pk)
           
            book_query = models.Book.object.filter(is_delete=False,pk__in=pks).all()
            if len(pks) != len(book_query):
                raise Exception('pk对应的数据不存在')     
        except Exception as e:
            return Response({'detail':f'{e}'},status=400)
        
        book_ser = serializer.BookModelSerializer(instance=book_query,data=request.data,many=True)
        book_ser.is_valid(raise_exception=True)
        book_list = book_ser.save()
        return APIResponse(results=serializer.BookModelSerializer(book_list,many=True).data)

(五)局部单改群改

局部更改和整体更改的唯一区别就在于partial属性值

  1. partial属性:序列化类中的partial=True,会让参与反序列化的字段都会置为选填字段
    • 提供了值就改变,没提供的字段就不修改
  2. context属性:将context的值(字典类型)封装到序列化对象中
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,反序列化的字段都会为选填字段,因此达到局部更改的目的
        book_ser = serializers.BookModelSerializer(instance=book_instance, data=request.data, partial=True)
        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)
        #  通过context将其传入、在校验方法中,可以通过self.context就能拿到传入的值,比如request对象
        book_ser = serializers.BookModelSerializer(instance=book_query, data=request_data, many=True, partial=True,context={'request': request})
        book_ser.is_valid(raise_exception=True)
        book_list = book_ser.save()
        return APIResponse(results=serializers.BookModelSerializer(book_list, many=True).data)

猜你喜欢

转载自www.cnblogs.com/wick2019/p/12117053.html