如何实现DRF中的过滤、排序、异常处理及Response封装?

2026-05-17 05:551阅读0评论SEO资讯
  • 内容介绍
  • 相关推荐

本文共计1905个文字,预计阅读时间需要8分钟。

目录+过滤和排序(4星)+内置过滤类+使用方法+第三方插件过滤(可含and关系)+使用方法+自定义过滤类+使用方法+自定义模糊查询且有and关系+源码分析+内置排序类(已有排序,又有过滤)

目录
  • 过滤和排序(4星)
    • 内置过滤类
      • 使用方法
    • 第三方插件过滤(可以有and关系)
      • 使用方法
    • 自定义过滤类
      • 使用方法
      • 自定义模糊查询且有and关系
      • 源码分析
    • 内置排序类(既有排序,又有过滤)
      • 使用方法
      • 过滤加排序
  • 异常处理(4星)
    • 使用方法
    • 源码分析
  • 二次封装Response

过滤和排序(4星)

查询所有才需要过滤(根据过滤条件),排序(按某个规律排序) 使用前提: 必须继承的顶层类是GenericAPIView, 所有指定的过滤字段都是和queryset对象里有的字段有关(即和对象所在的model表中所有的字段有关),和写序列化类里的字段无关

内置过滤类

内置过滤类使用,在视图类中配置,是模糊查询, 不能单独查询某个字段,是在视图类中配置的所有字段中的数据中模糊查询eg:在视图类中配置了2个字段,那么就会在这两个字段中模糊查询 使用前提: 必须继承的顶层类是GenericAPIView

使用方法

设置

from rest_framework.filters import SearchFilter class BookAPIView(ModelViewSet): queryset = Book.objects.all() serializer_class = BookModelSerializer # 在视图类中配置 最顶层的类至少是GenericAPIView filter_backends = [SearchFilter, ] # 设置这个 # 过滤条件,写多个就是过滤出多个字段中所有符合条件的数据 search_fields = ['title', 'price'] # 取决于models里的字段

查询

127.0.0.1:8000/books/?search=22 # 书名中或者价格中有22就能查询出来

第三方插件过滤(可以有and关系)

下载模块pip3 install django-filter 并且这个是精准查询, 可以指定表模型中的字段来精准查询, 多个字段连用是and关系 使用前提: 必须继承的顶层类是GenericAPIView

使用方法

注册

INSTALLED_APPS = [ ... 'django_filters', ]

设置

from django_filters.rest_framework import DjangoFilterBackend class BookAPIView(ModelViewSet): queryset = Book.objects.all() serializer_class = BookModelSerializer filter_backends = [DjangoFilterBackend, ] # 写上面导入的类 # 过滤条件,按名字过滤 filter_fields =['name', 'price']

查询

127.0.0.1:8000/books/?name=红 # 返回空列表 127.0.0.1:8000/books/?name=红楼梦 127.0.0.1:8000/books/?name=红楼梦&price=33 # &符号是and关系 127.0.0.1:8000/books/?name=红楼梦&price=32 # 返回空列表

自定义过滤类 使用方法

写一个类,继承BaseFilterBackend 基类,重写filter_queryset方法,然后返回的queryset对象,就是过滤后的对象 使用前提: 必须继承的顶层类是GenericAPIView

# 在自己创建的untils.py中 from rest_framework.filters import BaseFilterBackend class BookFilter(BaseFilterBackend): def filter_queryset(self, request, queryset, view): query = request.query_params.get('title') if query: queryset = queryset.filter(title__contains=query) # 根据过滤条件筛选数据 return queryset # 在views.py中 from .untils import BookFilter class BookAPIView(GenericViewSet, ListModelMixin): queryset = Book.objects.all() serializer_class = BookModelSerializer filter_backends = [BookFilter, ] # 设置自定义过滤类 自己定义的过滤类就不需要写类属性了

查询

127.0.0.1:8000/books/?title=红 # 模糊匹配 ,自己定义的

自定义模糊查询且有and关系

class MyFilter(BaseFilterBackend): def filter_queryset(self, request, queryset, view): # 因为我们不知道筛选条件是什么 所以要用Q方法来把用户输入的字段变成过滤条件 q = Q() # 从get请求中获取用户输入的筛选字段及条件 for field, query in request.query_params.items(): # 如果该字段不在model表中则报错 if not hasattr(Book, field): raise APIException('没有%s字段' % field) # 有则字段加模糊查询 field += '__contains' q.children.append((field, query)) queryset = Book.objects.filter(q) return queryset

源码分析

#### 源码分析----》GenericAPIView----》查询所有,调用了list---》self.filter_queryset(self.get_queryset())----》查看GenericAPIView的filter_queryset方法: def filter_queryset(self, queryset): for backend in list(self.filter_backends): # 去视图类里取自定义过滤类名 # 自定义过滤类名实例化得到对象调用filter_queryset方法 而这个就是我们自己写的方法,返回值就是过滤后的数据 queryset = backend().filter_queryset(self.request, queryset, self) return queryset 内置排序类(既有排序,又有过滤)

使用前提: 必须继承的顶层类是GenericAPIView

使用方法

设置

from rest_framework.filters import OrderingFilter,SearchFilter class BookAPIView(ModelViewSet): queryset = Book.objects.all() serializer_class = BookModelSerializer filter_backends = [SearchFilter, OrderingFilter] # 过滤条件,按名字过滤 search_fields = ['title', 'price'] # 按哪个字段排序 ordering_fields = ['price', 'id']

查询

127.0.0.1:8000/books/?ordering=price,-id # 上面这句话的意思是按价格升序,如果遇到相同的,就按id降序

过滤加排序

过滤和排序可以同时用--->因为他们本质是for循环一个个执行,所有优先使用过滤,再使用排序

filter_backends = [SearchFilter,OrderingFilter] ordering_fields=['price','id'] search_fields=['name','author']

查询

127.0.0.1:8000/books/?search=22&ordering=-id

异常处理(4星)

全局统一捕获异常,返回固定的格式 {code:999,msg:'未知错误'}(类似这种格式)

使用方法

在一个新的py文件内:

from rest_framework.views import exception_handler # 默认没有配置,出了异常会走它 from rest_framework.response import Response from utils.logging import logger # 把之前配的日志对象导过来 def common_exception_handler(exc, context): # 取出出错的IP,可以不用 print(context['request'].META.get('REMOTE_ADDR')) ######################################重点########################################## # exception_handler方法会返回Response对象或者None res = exception_handler(exc, context) if res: # 这表示已经处理了异常,它只处理APIExcepiton的异常 res = Response(data={'code': 998, 'msg': res.data.get('detail', '服务器异常')}) else: # 返回None,表示是出了不知道的异常 res = Response(data={'code': 999, 'msg': str(exc)}) # 注意:在这里,可以记录日志---》只要走到这,说明程序报错了,记录日志,以后查日志---》尽量详细 # 出错时间,错误原因,哪个视图类出了错,什么请求地址,什么请求方式出了错 request = context.get('request') # 这个request是当次请求的request对象 view = context.get('view') # 这个viewt是当次执行的视图类对象 # 记录错误日志 logger.error('错误原因:%s,错误视图类:%s,请求地址:%s,请求方式:%s' % (str(exc), str(view), request.path, request.method)) return res

配置文件配置:

REST_FRAMEWORK = { 'EXCEPTION_HANDLER': 'app01.untils.common_exception_handler' } 源码分析

二次封装Response

在utils/response.py文件内

from rest_framework.response import Response class APIResponse(Response): def __init__(self, code=100, msg='成功', status=None, headers=None, exception=False, content_type=None, template_name=None, **kwargs): res_data = { 'code': code, 'msg': msg, } # 如果传了其他的数据,组织成字典添加或更新到res_data字典内 if kwargs: res_data.update(kwargs) # 调用Response的__init__,把数据都传进去,其本质就是把返回数据用字典包装了一下 super().__init__(data=res_data, status=status, headers=headers, exception=exception, content_type=content_type, template_name=template_name)

在视图类内

from rest_framework.views import APIView from utils.response import APIResponse class TestAPIView(APIView): def get(self, request): return APIResponse(res=[{'name': 'lqz'}, {'name': 'egon'}], token='sadasd')

本文共计1905个文字,预计阅读时间需要8分钟。

目录+过滤和排序(4星)+内置过滤类+使用方法+第三方插件过滤(可含and关系)+使用方法+自定义过滤类+使用方法+自定义模糊查询且有and关系+源码分析+内置排序类(已有排序,又有过滤)

目录
  • 过滤和排序(4星)
    • 内置过滤类
      • 使用方法
    • 第三方插件过滤(可以有and关系)
      • 使用方法
    • 自定义过滤类
      • 使用方法
      • 自定义模糊查询且有and关系
      • 源码分析
    • 内置排序类(既有排序,又有过滤)
      • 使用方法
      • 过滤加排序
  • 异常处理(4星)
    • 使用方法
    • 源码分析
  • 二次封装Response

过滤和排序(4星)

查询所有才需要过滤(根据过滤条件),排序(按某个规律排序) 使用前提: 必须继承的顶层类是GenericAPIView, 所有指定的过滤字段都是和queryset对象里有的字段有关(即和对象所在的model表中所有的字段有关),和写序列化类里的字段无关

内置过滤类

内置过滤类使用,在视图类中配置,是模糊查询, 不能单独查询某个字段,是在视图类中配置的所有字段中的数据中模糊查询eg:在视图类中配置了2个字段,那么就会在这两个字段中模糊查询 使用前提: 必须继承的顶层类是GenericAPIView

使用方法

设置

from rest_framework.filters import SearchFilter class BookAPIView(ModelViewSet): queryset = Book.objects.all() serializer_class = BookModelSerializer # 在视图类中配置 最顶层的类至少是GenericAPIView filter_backends = [SearchFilter, ] # 设置这个 # 过滤条件,写多个就是过滤出多个字段中所有符合条件的数据 search_fields = ['title', 'price'] # 取决于models里的字段

查询

127.0.0.1:8000/books/?search=22 # 书名中或者价格中有22就能查询出来

第三方插件过滤(可以有and关系)

下载模块pip3 install django-filter 并且这个是精准查询, 可以指定表模型中的字段来精准查询, 多个字段连用是and关系 使用前提: 必须继承的顶层类是GenericAPIView

使用方法

注册

INSTALLED_APPS = [ ... 'django_filters', ]

设置

from django_filters.rest_framework import DjangoFilterBackend class BookAPIView(ModelViewSet): queryset = Book.objects.all() serializer_class = BookModelSerializer filter_backends = [DjangoFilterBackend, ] # 写上面导入的类 # 过滤条件,按名字过滤 filter_fields =['name', 'price']

查询

127.0.0.1:8000/books/?name=红 # 返回空列表 127.0.0.1:8000/books/?name=红楼梦 127.0.0.1:8000/books/?name=红楼梦&price=33 # &符号是and关系 127.0.0.1:8000/books/?name=红楼梦&price=32 # 返回空列表

自定义过滤类 使用方法

写一个类,继承BaseFilterBackend 基类,重写filter_queryset方法,然后返回的queryset对象,就是过滤后的对象 使用前提: 必须继承的顶层类是GenericAPIView

# 在自己创建的untils.py中 from rest_framework.filters import BaseFilterBackend class BookFilter(BaseFilterBackend): def filter_queryset(self, request, queryset, view): query = request.query_params.get('title') if query: queryset = queryset.filter(title__contains=query) # 根据过滤条件筛选数据 return queryset # 在views.py中 from .untils import BookFilter class BookAPIView(GenericViewSet, ListModelMixin): queryset = Book.objects.all() serializer_class = BookModelSerializer filter_backends = [BookFilter, ] # 设置自定义过滤类 自己定义的过滤类就不需要写类属性了

查询

127.0.0.1:8000/books/?title=红 # 模糊匹配 ,自己定义的

自定义模糊查询且有and关系

class MyFilter(BaseFilterBackend): def filter_queryset(self, request, queryset, view): # 因为我们不知道筛选条件是什么 所以要用Q方法来把用户输入的字段变成过滤条件 q = Q() # 从get请求中获取用户输入的筛选字段及条件 for field, query in request.query_params.items(): # 如果该字段不在model表中则报错 if not hasattr(Book, field): raise APIException('没有%s字段' % field) # 有则字段加模糊查询 field += '__contains' q.children.append((field, query)) queryset = Book.objects.filter(q) return queryset

源码分析

#### 源码分析----》GenericAPIView----》查询所有,调用了list---》self.filter_queryset(self.get_queryset())----》查看GenericAPIView的filter_queryset方法: def filter_queryset(self, queryset): for backend in list(self.filter_backends): # 去视图类里取自定义过滤类名 # 自定义过滤类名实例化得到对象调用filter_queryset方法 而这个就是我们自己写的方法,返回值就是过滤后的数据 queryset = backend().filter_queryset(self.request, queryset, self) return queryset 内置排序类(既有排序,又有过滤)

使用前提: 必须继承的顶层类是GenericAPIView

使用方法

设置

from rest_framework.filters import OrderingFilter,SearchFilter class BookAPIView(ModelViewSet): queryset = Book.objects.all() serializer_class = BookModelSerializer filter_backends = [SearchFilter, OrderingFilter] # 过滤条件,按名字过滤 search_fields = ['title', 'price'] # 按哪个字段排序 ordering_fields = ['price', 'id']

查询

127.0.0.1:8000/books/?ordering=price,-id # 上面这句话的意思是按价格升序,如果遇到相同的,就按id降序

过滤加排序

过滤和排序可以同时用--->因为他们本质是for循环一个个执行,所有优先使用过滤,再使用排序

filter_backends = [SearchFilter,OrderingFilter] ordering_fields=['price','id'] search_fields=['name','author']

查询

127.0.0.1:8000/books/?search=22&ordering=-id

异常处理(4星)

全局统一捕获异常,返回固定的格式 {code:999,msg:'未知错误'}(类似这种格式)

使用方法

在一个新的py文件内:

from rest_framework.views import exception_handler # 默认没有配置,出了异常会走它 from rest_framework.response import Response from utils.logging import logger # 把之前配的日志对象导过来 def common_exception_handler(exc, context): # 取出出错的IP,可以不用 print(context['request'].META.get('REMOTE_ADDR')) ######################################重点########################################## # exception_handler方法会返回Response对象或者None res = exception_handler(exc, context) if res: # 这表示已经处理了异常,它只处理APIExcepiton的异常 res = Response(data={'code': 998, 'msg': res.data.get('detail', '服务器异常')}) else: # 返回None,表示是出了不知道的异常 res = Response(data={'code': 999, 'msg': str(exc)}) # 注意:在这里,可以记录日志---》只要走到这,说明程序报错了,记录日志,以后查日志---》尽量详细 # 出错时间,错误原因,哪个视图类出了错,什么请求地址,什么请求方式出了错 request = context.get('request') # 这个request是当次请求的request对象 view = context.get('view') # 这个viewt是当次执行的视图类对象 # 记录错误日志 logger.error('错误原因:%s,错误视图类:%s,请求地址:%s,请求方式:%s' % (str(exc), str(view), request.path, request.method)) return res

配置文件配置:

REST_FRAMEWORK = { 'EXCEPTION_HANDLER': 'app01.untils.common_exception_handler' } 源码分析

二次封装Response

在utils/response.py文件内

from rest_framework.response import Response class APIResponse(Response): def __init__(self, code=100, msg='成功', status=None, headers=None, exception=False, content_type=None, template_name=None, **kwargs): res_data = { 'code': code, 'msg': msg, } # 如果传了其他的数据,组织成字典添加或更新到res_data字典内 if kwargs: res_data.update(kwargs) # 调用Response的__init__,把数据都传进去,其本质就是把返回数据用字典包装了一下 super().__init__(data=res_data, status=status, headers=headers, exception=exception, content_type=content_type, template_name=template_name)

在视图类内

from rest_framework.views import APIView from utils.response import APIResponse class TestAPIView(APIView): def get(self, request): return APIResponse(res=[{'name': 'lqz'}, {'name': 'egon'}], token='sadasd')