drf05(代码片段)

bigb bigb     2023-05-01     378

关键词:

Response封装

经过前面的学习我们可以发现, 我们每次成功响应了前端的请求, 都要按照固定的格式写一遍Response内部包含的数据,并将其返回给前端, 每返回一次就要完整的写一次, 是不是有点麻烦?

我们可以通过对Response进行简单封装, 来简化我们的代码

# 封装前
return Response(
    'status': 0,
    'msg': 'ok',
    'results': serializer_obj.data
)

# 封装后
return APIResponse(results=serializer_obj.data)
# 在app文件下新建一个response.py文件
from rest_framework.response import Response


# 定义APIResponse继承Response
class APIResponse(Response):
    # 重写__init__方法
    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
        
        if results is not None:
            data['results'] = results

        data.update(**kwargs)

        # 调用父类Response的__init__方法
        super().__init__(data=data, status=http_status, headers=headers, exception=exception, content_type=content_type)

深度查询子depth

  • depth是深度查询的一种实现方式
  • 在序列化类中的配置类中设置depth
  • 会根据对应深度的外键字段的主键值, 获取对应的记录
class PressModerSerializer(serializers.ModelSerializer):
  
    class Meta:
        model = models.Press
        fields = ['name', 'addr', 'books']
        # 设置查询深度为1
        depth = 1
        
        
-----------------------------------------------------------------------------------------------------



    "status": 0,
    "msg": "ok",
    "results": [
        
            "name": "东方出版社",
            "addr": "上海",
            # 不设置查询深度, 显示的是主键值; 设置查询深度, 显示是主键值对应的记录
            "books": [
                
                    "id": 1,
                    "is_delete": false,
                    "created_time": "2019-12-26T18:40:09",
                    "name": "三体",
                    "price": "49.90",
                    # 如果设置depth=2, 那下面id=1的press也会被查出来 
                    "press": 1,
                    "authors": [
                        1
                    ]
                ,
                
                    "id": 3,
                    "is_delete": false,
                    "created_time": "2019-12-26T18:42:08",
                    "name": "球状闪电",
                    "price": "36.60",
                    "press": 1,
                    "authors": [
                        1
                    ]
                
            ]
        ,
        

深度查询之自定义@property方法

  • 在模型类中自定义@property属性方法获取外键字段对应的数据, 也可以实现深度查询
  • 外键字段值只对应一条记录的深度查询: 自定义方法只需要返回一个值
  • 外键字段值对应多条记录的深度查询: 自定义方法需要返回多个值
# models.py
class Book(Base):
    name = models.CharField(max_length=64)
    price = models.DecimalField(max_digits=6, decimal_places=2)
    press = models.ForeignKey(to='Press', related_name='books', db_constraint=False, on_delete=models.SET_NULL,
                              null=True)
    authors = models.ManyToManyField(to='Author', related_name='books', db_constraint=False)

    # 外键字段只对应一条数据时
    @property
    def book_press(self):
        # 这里也可以利用序列化类
        # from serializers import PressModelSerializer
        # return  PressModerSerializer(self.press).data
        
        return 
            'name': self.press.name,
            'addr': self.press.addr
        

    # 外键字段对应多条数据时
    @property
    def book_authors(self):
        authors = []
        for author in self.authors.all():

            author_dic = 
                'name': author.name,
            

            # 如果作者没有详情, 进行异常捕获
            try:
                author_dic['mobile'] = author.detail.moblie
            except:
                author_dic['mobile'] = '无'

            authors.append(author_dic)

        return authors
        
    
-----------------------------------------------------------------------------------------------------


# serializers.py
class BookMethodSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Book
        # 将自定义的@property属性方法在fields中配置
        fields = ['name', 'price', 'book_press', 'book_authors']

delete请求实现单删群删

  • 将单删变为群删一条数据: pks = [pk, ]
  • 对涉及数据库内部操作的代码进行异常处理
 # 单删群删
    def delete(self, request, *args, **kwargs):
        """
        单删
            接口: /book/(pk)/, 数据: 空
        群删
            接口: /book/, 数据: [pk1, pk2, ...]
        """
        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, 'invalid data', http_status=400)

        if rows:
            return APIResponse(0, 'delete ok')
        else:
            return APIResponse(1, 'delete fail')

post请求实现单增群增

  • 单增群增的接口都是 /book/
  • 单增请求携带数据的格式是字典, 群增请求携带数据的格式是列表套字典
  • 通过判断请求携带数据的数据类型, 来确定many=True or False
# 单增群增
    def post(self, request, *args, **kwargs):
        """
        单增
            接口: /book/, 数据: ...
        群增
            接口: /book/, 数据: [,, ...]
        """
        if isinstance(request.data, dict):
            is_many = False
        elif isinstance(request.data, list):
            is_many = True
        else:
            return APIResponse(0, 'invalid data', http_status=400)

        serializer_obj = serializers.BookModelSerializer(data=request.data, many=is_many)
        serializer_obj.is_valid(raise_exception=True)
        book_obj_or_list = serializer_obj.save()

        return APIResponse(results=serializers.BookModelSerializer(book_obj_or_list, many=is_many).data)

ListSerializer

  • ModelSerializer的create的方法只能进行单增操作
  • ModelSerializer默认配置了ListSerializer来辅助其完成群增操作
  • ListSerialzer下面create的方法只是对群增数据进行了遍历, 然后调用ModelSerializer的create方法进行数据的入库
# ModelSerializer下面的create的方法只能实现单增
def create(self, validated_data):
    (...)    
    return instance
# ListSerializer下面的create方法
def create(self, validated_data):
    return [
        # 对群增数据进行遍历, 遍历一个, 就调用ModelSerializer的create方法来增加一个
        # self.child就是ModelSerializer对象
        self.child.create(attrs) for attrs in validated_data
        ]
  • 如果只是进行群增操作, 我们是没有必要自定义ListSerializer子类, 重写create方法的 (当然确实可以写, 但没必要)
  • 如果进行群改操作, 就需要我们自定义ListSerializer子类, 重写update方法
# ListSerializer的update方法
def update(self, instance, validated_data):
    raise NotImplementedError(
        "Serializers with many=True do not support multiple update by "
        "default, only multiple create. For updates it is unclear how to "
        "deal with insertions and deletions. If you need to support "
        "multiple update, use a `ListSerializer` class and override "
        "`.update()` so you can specify the behavior exactly."
    )

put请求实现整体单改和整体群改

  • 群改请求携带数据的数据格式是列表套字典, 且每个字典都必需包含pk
  • 如果有一个字典没有包含pk, 或者pk没有对应的数据, 就直接整体报错
  • 需要借助自定义的ListSerializer类, 重新update方法来实现群改操作
# views.py
# 整体单改群改 
def put(self, request, *args, **kwargs):
    """
    单改
        接口: /book/(pk)/, 数据: ...
    群改
        接口: /book/, 数据: ['pk':1,..,'pk':2,.., ...]
    """
    pk = kwargs.get('pk')
    # 单改
    if pk:
        try:
            # 注意这里我们使用get方法, 没有的话就报错, 进行异常处理
            book_instance = models.Book.objects.get(is_delete=False, pk=pk)

        except:
            return APIResponse(1, 'invalid data', http_status=400)

        serializer_obj = serializers.BookModelSerializer(instance=book_instance, data=request.data)
        serializer_obj.is_valid(raise_exception=True)
        book_obj = serializer_obj.save()

        return APIResponse(results=serializers.BookModelSerializer(book_obj).data)

    # 群改
    else:
        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 not len(book_query) == len(book_query):
                raise Exception('pk error')

        except Exception as e:
            return APIResponse(1, msg=f'e error', http_status=400)

        serializer_obj = serializers.BookModelSerializer(instance=book_query, data=request.data, many=True)
        serializer_obj.is_valid(raise_exception=True)
        book_list = serializer_obj.save()

        return APIResponse(results=serializers.BookModelSerializer(book_list, many=True).data)
# serializers.py
# 自定义ListSerializer子类实现群改操作
class BookListSerializer(serializers.ListSerializer):
    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):
    class Meta:
        # 配置自定义的ListSerializer类
        list_serializer_class = BookListSerializer

        model = models.Book
        fields = ['name', 'price', 'book_press', 'book_authors', 'press', 'authors']
        extra_kwargs = 
            'press': 
                'write_only': True
            ,
            'authors': 
                'write_only': True
            
        

patch请求实现局部单改和局部整改

  • 局部改就是在实例化serializer对象的时候加一个partial=True参数就行
    • 某个字段被提供了值, 则该字段修改
    • 某个字段没有被提供值, 则保留原有的值
  • 实例化serializer对象时设置context参数, 可以将视图类中的数据传递给序列化类下面的钩子函数
  • 局部改是兼容整体改的, 因此我们以后用patch请求进行修改操作就好了
# views.py
# 局部单改群改 
def put(self, request, *args, **kwargs):
    """
    单改
        接口: /book/(pk)/, 数据: ...
    群改
        接口: /book/, 数据: ['pk':1,..,'pk':2,.., ...]
    """
    pk = kwargs.get('pk')
    # 单改
    if pk:
        try:
            # 注意这里我们使用get方法, 没有的话就报错, 进行异常处理
            book_instance = models.Book.objects.get(is_delete=False, pk=pk)

        except:
            return APIResponse(1, 'invalid data', http_status=400)
        
        # 实例化serializer对象时添加partial=True
        serializer_obj = serializers.BookModelSerializer(instance=book_instance, data=request.data, partial=True)
        serializer_obj.is_valid(raise_exception=True)
        book_obj = serializer_obj.save()

        return APIResponse(results=serializers.BookModelSerializer(book_obj).data)

    # 群改
    else:
        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 not len(book_query) == len(book_query):
                raise Exception('pk error')

        except Exception as e:
            return APIResponse(1, msg=f'e error', http_status=400)
        
        # 实例化serializer对象时添加partial=True
        serializer_obj = serializers.BookModelSerializer(instance=book_query, data=request.data, many=True, partial=True)
        serializer_obj.is_valid(raise_exception=True)
        book_list = serializer_obj.save()

        return APIResponse(results=serializers.BookModelSerializer(book_list, many=True).data)

drf05(代码片段)

...麻烦?我们可以通过对Response进行简单封装,来简化我们的代码#封装前returnResponse('status':0,'msg':'ok','results':serializer_obj.data)#封装后returnAPIResponse(results=serializer_obj.data)#在app文件下新建一个response.py文件fromrest_fr... 查看详情

drf——认证(代码片段)

drf认证官网地址:https://www.django-rest-framework.org/api-guide/requests/1.drf的执行流程与源码剖析fromrest_framework.viewsimportAPIViewclassStudentView(APIView):defget(self,request,*args,**kwargs):pass说明:图片上的settings 查看详情

2.drf入门(代码片段)

drf介绍DjangoRESTframework(简称:DRF)是一个强大而灵活的WebAPI工具。遵循RESTFullAPI风格,功能完善,可快速开发API平台。官网文档:https://www.django-rest-framework.orgDjangoRESTframework最新版使用要求:Python(3.6、3.7、3.8、3.9、3.10)Django(2.2、3.0、3.1... 查看详情

drf路由(代码片段)

"在urls.py文件中按照如下步骤写,即可正确使用DRF的内置路由.from.viewsimportBookModel#1.导入我们的视图fromrest_framework.routersimportDefaultRouter#2.导入rest_framework内置的路由方法router=DefaultRouter()#3.首先,实例化一个DefaultRouter对象router 查看详情

drf缓存解决方案drf-extensions/redis(代码片段)

drf-extensions概述drf-extensions组件内部提供了DRF 的本地内存方式的缓存方式本地内存方式缓存在项目重启后则会消失官方点击这里  安装pip3installdrf-extensionsorfromgithubpip3installhttps://github.com/chibisov/drf-extensions/archive/maste 查看详情

19-djangorestframework-drf工程搭建(代码片段)

DRF工程搭建前言环境安装与配置安装DRF注册DRF应用DRF体验1.创建序列化器2.编写视图3.定义路由4.运行测试前言本篇来学习DRF工程搭建及体验环境安装与配置DRF需要以下依赖:Python(2.7,3.4,3.5,3.6,3.7)Django(1.11,2.0,2.1)DRF是以Django扩展... 查看详情

drf-分页(代码片段)

目录一、三种分页模式1PageNumberPagination2LimitOffsetPagination3CursorPagination二、继承APIView的视图类下写分页一、三种分页模式1PageNumberPaginationfromrest_framework.paginationimportPageNumberPagination,LimitOffsetPagination,Curs 查看详情

初识drf(代码片段)

....1.1创建django项目6.2添加rest_framework应用6.3体验drf完全简写代码的过程6.3.0创先子应用6.3.1.创建模型操作类6.3.1.1执行数据迁移6.3.2.创建序列化器6.3.3.编写视图6.3.4.定义路由6 查看详情

drf序列化(代码片段)

一、安装Django RESTframework框架使用命令:pipinstalldjangorestframework二、在setings里面注册INSTALLED_APPS=["rest_framework"]Serializers序列化组件 查看详情

drf分页器(代码片段)

drf分页器1.第一种分页:类似于django中的分页2.第二种分页:偏移分页3.第三种分页:加密分页(查询速度快)无法跳跃基本参数fromrest_framework.paginationimportPageNumberPagination,LimitOffsetPagination,CursorPaginationpage_size#每页显示的数量page_query_param=... 查看详情

drf分页(代码片段)

 restframework中提供三种分页:fromrest_framework.paginationimportPageNumberPagination,LimitOffsetPagination,CursorPagination 全局配置文件:REST_FRAMEWORK=‘PAGE_SIZE‘:21.分页,看第n页,每页显示n条数据例如:http://127.0.0. 查看详情

drf之分页器(代码片段)

一、简介:drf内置了三种分页器类,一般需要重写类继承默认的分页器类来定制属性的具体数值。二、PageNumberPagination  1、路径:rest_framework.pagination.PageNumberPagination。  2、重写类:classNewPageNumberPagination(PageNumberPagination):page... 查看详情

12.drf-节流(代码片段)

Djangorestframework源码分析(3)----节流添加节流自定义节流的方法限制60s内只能访问3次(1)API文件夹下面新建throttle.py,代码如下:#utils/throttle.pyfromrest_framework.throttlingimportBaseThrottleimporttimeVISIT_RECORD=#保存访问记录classVisitThrottl 查看详情

drf框架(代码片段)

DRF框架知识总览一、接口(api):什么是接口接口文档接口规范二、FBV=>CBV:Function|ClassBaseViewCBV的请求生命周期CBV比FBV的优势三、drf框架的基础试图类APIView:请求模块、解析模块、渲染模块、响应模块、异常模块四、drf核心组件... 查看详情

drf之jwt补充(代码片段)

DRF之JWT补充1.JWT控制用户登录后才能反问,匿名用户无法访问classQueryUserView(GenericViewSet,RetrieveModelMixin):"""查询接口"""queryset=User.objects.all()serializer_class=UserSerializerpk=None#thrott 查看详情

drf-路由和认证(代码片段)

drf-路由目录drf-路由1路由1.1路由router的使用1.2action的使用2认证2.1认证的写法2.2源码分析2.3认证组件的使用1路由针对视图集ViewSet,我们出来可以自己手动指明请求方式与执行函数间的对应关系,还可以使用Routers来快速实现路由... 查看详情

drf-解析器组件(代码片段)

DRF之解析器组件引入DjangoRestFramework帮助我们实现了处理application/json协议请求的数据,另外,我们也提到,如果不使用DRF,直接从request.body里面拿到原始的客户端请求的字节数据,经过decode,然后json反序列化之后,也可以得到一... 查看详情

drf概述(代码片段)

DRF概述一.REST1.什么是编程?数据结构和算法的结合2.什么是REST?回顾曾经做过的图书管理系统,我们是这样设计URL的:  127.0.0.1:9001/books/  127.0.0.1:9001/get_all_books/访问所有的数据127.0.0.1:9001/books/id/127.0.0.1:9001/books/id?method=get访问单... 查看详情