drf 過濾、排序、分頁、異常處理

內(nèi)容概要

  • 過濾
  • 排序
  • 分頁
  • 異常處理

內(nèi)容詳細(xì)

過濾

過濾是涉及到查詢數(shù)據(jù)的接口才需要過濾功能

公司主營業(yè)務(wù):網(wǎng)站制作、網(wǎng)站建設(shè)、移動網(wǎng)站開發(fā)等業(yè)務(wù)。幫助企業(yè)客戶真正實(shí)現(xiàn)互聯(lián)網(wǎng)宣傳,提高企業(yè)的競爭能力。創(chuàng)新互聯(lián)是一支青春激揚(yáng)、勤奮敬業(yè)、活力青春激揚(yáng)、勤奮敬業(yè)、活力澎湃、和諧高效的團(tuán)隊。公司秉承以“開放、自由、嚴(yán)謹(jǐn)、自律”為核心的企業(yè)文化,感謝他們對我們的高要求,感謝他們從不同領(lǐng)域給我們帶來的挑戰(zhàn),讓我們激情的團(tuán)隊有機(jī)會用頭腦與智慧不斷的給客戶帶來驚喜。創(chuàng)新互聯(lián)推出正定免費(fèi)做網(wǎng)站回饋大家。

DRF 中使用的過濾方式:

  • 1、 內(nèi)置過濾類 在請求數(shù)據(jù)中用“search=字符”條件過濾(模糊查詢)
  • 2、 第三方過濾類 在請求數(shù)據(jù)中用“字段名=字符”條件過濾 (嚴(yán)格查詢)
  • 3、 自定義過濾類

內(nèi)置過濾類

使用模塊: from rest_framework.filters import SearchFilter

在視圖層中使用內(nèi)置過濾類

前提:需要使用 GenericAPIView 類中的filter_backends屬性,所以視圖類得繼承 GenericAPIView

class GenericAPIView(views.APIView):
    queryset = None
    serializer_class = None
    lookup_field = 'pk'
    lookup_url_kwarg = None
    filter_backends = api_settings.DEFAULT_FILTER_BACKENDS
    pagination_class = api_settings.DEFAULT_PAGINATION_CLASS

步驟

  1. 視圖類內(nèi)filter_backends中使用SearchFilter
  2. 類屬性search_fields指定過濾的字段
from rest_framework.filters import SearchFilter


class BookViewSet(ViewSetMixin, ListAPIView):
    queryset = models.Book.objects
    serializer_class = BookModelSerializer
    authentication_classes = [LoginAuth, ]
    # throttle_classes = [IPThrottling, ]
    filter_backends = [SearchFilter]
    search_fields = ['name', 'price', ]

如果是過濾外鍵字段:

使用雙下滑的正向查詢方式

class BookViewSet(ViewSetMixin, ListAPIView):
    queryset = models.Book.objects
    serializer_class = BookModelSerializer
    authentication_classes = [LoginAuth, ]
    # throttle_classes = [IPThrottling, ]
    filter_backends = [SearchFilter, ]
    search_fields = ['publish__name', ]

總結(jié)

  • 內(nèi)置過濾類的使用,模糊查詢會將包含過濾字段的數(shù)據(jù)都過濾出來,前提是在search_fields列表內(nèi)指定的字段;
  • 內(nèi)置過濾的特點(diǎn)是模糊查詢
  • 過濾字段參數(shù)為search
  • 過濾外鍵字段,使用雙下滑的正向查詢方式

第三方過濾類

1、安裝: pip install django-filter

2、使用模塊: from django_filters.rest_framework import DjangoFilterBackend

3、在項目配置文件 settings.py 中注冊下載的 app

4、第三方過濾類在filter_backends字段中寫,filter_fields字段指定過濾的字段

INSTALLED_APPS = [
    ...
    'django_filters',  # 需要注冊應(yīng)用,
]

4、視圖層中使用

class BookViewSet(ViewSetMixin, ListAPIView):
    queryset = models.Book.objects
    serializer_class = BookModelSerializer
    authentication_classes = [LoginAuth, ]
    filter_backends = [DjangoFilterBackend, ]
    filter_fields = ['name', 'price']

總結(jié)

  • 第三方過濾類在filter_backends字段中寫,filter_fields字段指定過濾的字段
  • 第三方過濾類不支持模糊查詢,是精準(zhǔn)匹配
  • 第三方過濾類的使用,視圖類也必須繼承GenericAPIView才能使用
  • 在鏈接內(nèi)通過&來表示和的關(guān)系

外鍵字段怎么查?

自定義過濾類

1、新建一個過濾文件,寫一個類繼承BaseFilterBackend,重寫filter_queryset(self, request, queryset, view)方法,返回queryset對象,qs對象是過濾后的

2、視圖層使用,只需要指定filter_backend屬性為自定義類列表

3、查詢過濾,支持模糊查詢(自己定制過濾方式),在filter_queryset方法自定義過濾規(guī)則

自定義過濾類的書寫:

from rest_framework.filters import BaseFilterBackend
from django.db.models import Q


class Myfilter(BaseFilterBackend):
    def filter_queryset(self, request, queryset, view):
        # 獲取過濾參數(shù)
        qs_name = request.query_params.get('name')
        qs_price = request.query_params.get('price')
        # title__contains:精確大小寫查詢,SQL中-->like BINARY
        # 利用Q查詢構(gòu)造或關(guān)系
        if qs_name:
            queryset = queryset.filter(name__contains=qs_name)
        elif qs_price:
            queryset = queryset.filter(price__contains=qs_price)
        elif qs_name or qs_price:
            queryset = queryset.filter(Q(name__contains=qs_name) | Q(price__contains=qs_price))
        return queryset

視圖類:

from app01.filter import Myfilter


class BookViewSet(ViewSetMixin, ListAPIView):
    queryset = models.Book.objects
    serializer_class = BookModelSerializer
    authentication_classes = [LoginAuth, ]
    filter_backends = [Myfilter, ]

源碼分析

我們知道過濾的前提條件是視圖繼承了GenericAPIView才能使用,那么在GenericAPIView中的執(zhí)行流程是什么?

1、調(diào)用了GenericAPIView中的filter_queryset方法
2、filter_queryset方法源碼:
    def filter_queryset(self, queryset):
        for backend in list(self.filter_backends):
            queryset = backend().filter_queryset(self.request, queryset, self)
        return queryset
'''
1.backend是通過遍歷該類的filter_backends列表的得到的,也就是我們指定的過濾類列表,那么backend就是我們的過濾類
2.通過實(shí)例化得到對象來調(diào)用了類內(nèi)的filter_queryset返回了過濾后的對象
'''

排序

使用模塊:from rest_framework.filters import OrderingFilter

步驟

  1. 視圖類中配置,且視圖類必須繼承GenericAPIView
  2. 與過濾類一樣要把排序類存入 filter_backends 屬性的列表中
  3. 通過ordering_fields指定要排序的字段
  4. 排序過濾,-號代表倒序,且必須使用ordering指定排序字段

視圖類書寫:

from app01.filter import Myfilter
from rest_framework.filters import OrderingFilter


class BookViewSet(ViewSetMixin, ListAPIView):
    queryset = models.Book.objects
    serializer_class = BookModelSerializer
    authentication_classes = [LoginAuth, ]
    # throttle_classes = [IPThrottling, ]
    filter_backends = [Myfilter, OrderingFilter, ]  # 先過濾后排序
    ordering_fields = ['id', 'price']

注意:過濾可以和排序同時使用,但是先執(zhí)行過濾再執(zhí)行排序,提升了代碼的效率(先過濾后排序),因?yàn)槿绻扰判?,那么?shù)據(jù)庫的數(shù)量龐大的話,直接操作了整個數(shù)據(jù)庫,消耗資源,過濾完成后排序只是針對一小部分?jǐn)?shù)據(jù)

分頁

分頁只在查詢所有接口中使用

導(dǎo)入分頁類: from rest_framework.pagination import PageNumberPagination,LimitOffsetPagination,CursorPagination

DRF 中分頁的三種方式:

  • 1、PageNumberPagination,基本分頁
  • 2、LimitOffsetPagination,偏移分頁
  • 3、CursorPagination,游標(biāo)分頁

PageNumberPagination

步驟

自定義類,繼承PageNumberPagination,重寫四個類屬性

  • page_size:設(shè)置每頁默認(rèn)顯示的條數(shù)
  • page_query_param:url中的查詢條件,books/?page=2表示第二頁
  • page_size_query_param:每頁顯示多少條的查詢條件,books/?page=2&size=5,表示查詢第二頁,顯示5條
  • max_page_size:設(shè)置每頁最多顯示條數(shù),不管查多少條,最大顯示該值限制的條數(shù)

注意: 配置在視圖類中,通過pagination_class指定,必須繼承GenericAPIView才有

分頁類書寫:

from rest_framework.pagination import PageNumberPagination


class BookPagination(PageNumberPagination):
    page_size = 3  # 默認(rèn)每頁顯示2條
    page_query_param = 'page'  # 查詢條件,eg:page=3
    page_size_query_param = 'size'  # 查詢條件參數(shù)size=5顯示五條
    max_page_size = 10  # 每頁最大顯示條數(shù)

視圖層類:

pagination_class 屬性賦值分頁類

from app01.page import BookPagination


class BookViewSet(ViewSetMixin, ListAPIView):
    queryset = models.Book.objects.all()
    serializer_class = BookModelSerializer
    authentication_classes = []
    filter_backends = [Myfilter, OrderingFilter, ]  # 先過濾后排序
    ordering_fields = ['id', 'price']
    pagination_class = BookPagination

LimitOffsetPagination

步驟

  1. 自定義類,繼承LimitOffsetPagination,重寫四個類屬性
    • default_limit:默認(rèn)每頁獲取的條數(shù)
    • limit_query_param:每頁顯示多少條的查詢條件,比如?limit=3,表示獲取三條,如果不寫默認(rèn)使用default_limit設(shè)置的條數(shù)
    • offset_query_param:表示偏移量參數(shù),比如?offset=3表示從第三條開始往后獲取默認(rèn)的條數(shù)
    • max_limit:設(shè)置最大顯示條數(shù)
  2. 視圖類內(nèi)配置,pagination_class參數(shù)指定,必須繼承GenericAPIView才有

分頁類書寫:

from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination

class MyLimitOffset(LimitOffsetPagination):
    default_limit = 2  # 默認(rèn)每頁顯示2條
    limit_query_param = 'limit'  # ?limit=3,查詢出3條
    offset_query_param = 'offset'  # 偏移量,?offset=2,從第2條后開始
    max_limit = 5  # 最大顯示5條

視圖層類:

pagination_class 屬性賦值分頁類

from app01.page import BookPagination, MyLimitOffset


class BookViewSet(ViewSetMixin, ListAPIView):
    queryset = models.Book.objects.all()
    serializer_class = BookModelSerializer
    authentication_classes = []
    filter_backends = [Myfilter, OrderingFilter, ]  # 先過濾后排序
    ordering_fields = ['id', 'price']
    pagination_class = MyLimitOffset

CursorPagination

步驟

  1. 自定義類,繼承CursorPagination,重寫三個類屬性
    • page_size:每頁顯示的條數(shù)
    • cursor_query_param:查詢條件
    • ordering:排序規(guī)則,指定排序字段
  2. 視圖類內(nèi)配置,pagination_class參數(shù)指定,必須繼承GenericAPIView才有

分頁類書寫:

from rest_framework.pagination import CursorPagination

class MyCursor(CursorPagination):
    page_size = 3
    cursor_query_param = 'cursor'
    ordering = 'id'

視圖層類:

pagination_class 屬性賦值分頁類

from app01.page import BookPagination, MyLimitOffset, MyCursor

class BookViewSet(ViewSetMixin, ListAPIView):
    queryset = models.Book.objects.all()
    serializer_class = BookModelSerializer
    authentication_classes = []
    filter_backends = [Myfilter, ]  # 使用了 cursor 游標(biāo)分頁,不要指定排序規(guī)則,會報錯
    ordering_fields = ['id', 'price']
    pagination_class = MyCursor

查詢方式

http://127.0.0.1:8000/books/?cursor=cD02

注意:分頁類內(nèi)指定了排序,視圖內(nèi)不要寫排序規(guī)則,不然報錯

  • 跟上面兩種的區(qū)別:上面兩種,可以從中間位置獲取某一頁,Cursor方式只能上一頁和下一頁
  • 下面這種方式,先排序,內(nèi)部維護(hù)了一個游標(biāo),游標(biāo)只能選擇往前走或往后走,在取某一頁的時候,不需要過濾之前的數(shù)據(jù)
  • 這種分頁方式特殊,只能選擇上一頁和下一頁,不能指定某一頁,但是速度快,適合大數(shù)據(jù)量的分頁
  • 大數(shù)據(jù)量和app分頁---》下拉加載下一頁,不需要指定跳轉(zhuǎn)到第幾頁

異常處理

之前讀APIView源碼的時候,捕獲了全局異常,在執(zhí)行三大認(rèn)證,視圖類的方法時候,如果出了異常,會被全局異常捕獲

以下是APIView捕獲異常 的流程

1、 APIView源碼
# dispatch方法源碼
    except Exception as exc:
         response = self.handle_exception(exc)
# handle_exception方法源碼
    exception_handler = self.get_exception_handler()
    response = exception_handler(exc, context)

2、 默認(rèn)配置文件
get_exception_handler() 調(diào)用的是 views 中的 exception_handler

'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler',


3、views種的exception_handler方法
def exception_handler(exc, context):
    if isinstance(exc, Http404):
        exc = exceptions.NotFound()
    elif isinstance(exc, PermissionDenied):
        exc = exceptions.PermissionDenied()
    if isinstance(exc, exceptions.APIException):
        headers = {}
        if getattr(exc, 'auth_header', None):
        else:
            data = {'detail': exc.detail}
        return Response(data, status=exc.status_code, headers=headers)

    return None

由上源碼可知,exception_handler(exc, context) 方法,如果報的是已知的錯會返回 Response 對象,未知錯誤返回 None

自定義異常

可以自定義出現(xiàn)異常之后的處理方法和返回數(shù)據(jù)的格式

  • exc:錯誤原因
  • context:字典,包含了當(dāng)前請求對象和視圖類對象

重寫異常處理方法:

from rest_framework.views import exception_handler
from rest_framework.response import Response


def myexception_handler(exc, context):
    # 先執(zhí)行原來的exception_handler幫助我們處理
    res = exception_handler(exc, context)
    if res:
        # res有值代表處理過了APIException對象的異常了,返回的數(shù)據(jù)再定制
        res = Response(data={'code': 998, 'msg': res.data.get('detail', '服務(wù)器異常,請聯(lián)系系統(tǒng)管理員')})
        # res = Response(data={'code': 998, 'msg': '服務(wù)器異常,請聯(lián)系系統(tǒng)管理員'})
        # res.data.get從響應(yīng)中獲取原來的處理詳細(xì)信息
    else:
        res = Response(data={'code': 999, 'msg': str(exc)})
        print(exc)  # list index out of range

    '''模擬日志處理'''
    request = context.get('request')  # 當(dāng)次請求的request對象
    view = context.get('view')  # 當(dāng)次執(zhí)行的視圖類對象
    print('錯誤原因:%s,錯誤視圖類:%s,請求地址:%s,請求方式:%s' % (str(exc), str(view), request.path, request.method))
    '''結(jié)果:
    錯誤原因:list index out of range,錯誤視圖類:<app01.views.TestView object at 0x000001C3B1C7CA58>,請求地址:/test/,請求方式:GET
    '''
    return res

修改異常的配置路徑:

REST_FRAMEWORK = {
    'EXCEPTION_HANDLER': 'app01.myexception.exception_handler' # 再出異常,會執(zhí)行自己定義的函數(shù)
}

視圖類中報錯就會自動觸發(fā)異常處理:

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.exceptions import APIException
# 測試異常視圖
class Test(APIView):
    def get(self,request):

        # 1、 其他報錯
        # l = [1,2,3]
        # print(l[100])

        # 2、APIException異常
        # raise APIException('APIException errors!')

        return Response('success!')

REST framework定義的異常

  • APIException 所有異常的父類
  • ParseError 解析錯誤
  • AuthenticationFailed 認(rèn)證失敗
  • NotAuthenticated 尚未認(rèn)證
  • PermissionDenied 權(quán)限決絕
  • NotFound 未找到
  • MethodNotAllowed 請求方式不支持
  • NotAcceptable 要獲取的數(shù)據(jù)格式不支持
  • Throttled 超過限流次數(shù)
  • ValidationError 校驗(yàn)失敗

當(dāng)前標(biāo)題:drf 過濾、排序、分頁、異常處理
標(biāo)題來源:http://m.kartarina.com/article20/dsoghco.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站設(shè)計公司App開發(fā)、網(wǎng)站收錄建站公司響應(yīng)式網(wǎng)站網(wǎng)站營銷

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

成都seo排名網(wǎng)站優(yōu)化
主站蜘蛛池模板: 中文字幕无码乱码人妻系列蜜桃 | 亚洲精品无码高潮喷水A片软| 亚洲AV无码码潮喷在线观看 | 免费a级毛片无码a∨蜜芽试看| 久久AV高潮AV无码AV| 亚洲国产精品无码一线岛国| 久久无码国产专区精品| 一级电影在线播放无码| 亚洲AV无码国产精品色午友在线| 成年男人裸j照无遮挡无码| 手机永久无码国产AV毛片| 无码精品人妻一区二区三区影院| 99久久亚洲精品无码毛片| 日韩精品无码免费专区网站| 人妻中文字幕AV无码专区| 人妻少妇乱子伦无码视频专区| 亚洲成A人片在线观看无码3D| 中文字幕人成无码人妻| 国产AV无码专区亚洲精品| 一本之道高清无码视频| 国产午夜无码片在线观看影院| 久久精品无码午夜福利理论片| 夜夜添无码试看一区二区三区| 无码h黄肉3d动漫在线观看| 亚洲AV无码专区国产乱码不卡| 亚洲色av性色在线观无码| 久久青青草原亚洲av无码app | 亚洲AV无码久久| 国产成人麻豆亚洲综合无码精品| 天码av无码一区二区三区四区| 亚洲一区AV无码少妇电影| 亚洲一区无码中文字幕乱码| 久久精品成人无码观看56| 国产激情无码一区二区app| AV无码精品一区二区三区宅噜噜| 免费A级毛片无码A∨男男| 国产成人无码精品久久久小说| 亚洲国产成人无码AV在线影院 | 亚洲成av人无码亚洲成av人 | 亚洲人成国产精品无码| 亚洲一级特黄无码片|