• 當前位置:首頁 > IT技術 > 編程語言 > 正文

    django-rest-framework 基礎四 過濾、排序、分頁、異常處理
    2022-05-11 10:59:27

    django-rest-framework 基礎四 過濾、排序、分頁、異常處理

    1. 過濾

    在之前所寫的五個接口中,只有獲取所有需要過濾,其他接口都不需要。如在訪問的時候帶參數過濾出自己想要的數據。

    http://127.0.0.1:8080/?search=活著
    

    1.1 內置過濾類

    views.py

    from rest_framework.viewsets import GenericViewSet
    from rest_framework.mixins import ListModelMixin
    
    from 應用名.models import Book  # 數據庫
    from 應用名.serializer import BookSerializer # 序列化器
    
    from rest_framework.filters import SearchFilter
    
    
    class BookView(GenericViewSet, ListModelMixin):
    
        queryset = Book.objects.all()
        serializer_class = BookSerializer
    
        filter_backends = [SearchFilter,]
        # 過濾name
        # search_fields = ['name']
    	# 過濾namt或author
        search_fields = ['name','author']
    

    路由urls.py

    from django.contrib import admin
    from django.urls import path,include
    from authenticated import views
    from rest_framework import routers
    
    router = routers.SimpleRouter()
    router.register('books', views.BookView,"books")
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('', include(router.urls))
    ]
    
    

    訪問(模糊匹配):

    http://127.0.0.1:8000/books/?search=西游記
    http://127.0.0.1:8000/books/?search=華
    

    image-20220406224215678

    image-20220406224259442

    1.2 第三方過濾類

    使用第三方過濾類,

    第一步先安裝django-filter

    pip install django-filter
    

    第二步在配置里注冊settings.py

    INSTALLED_APPS = [
    	...
        'rest_framework',
        'django_filters',
    ]
    

    第三步在視圖類中使用views.py

    from rest_framework.viewsets import GenericViewSet
    from rest_framework.mixins import ListModelMixin
    
    from 應用名.models import Book  # 數據庫
    from 應用名.serializer import BookSerializer # 序列化器
    
    from django_filters.rest_framework import DjangoFilterBackend
    class BookView(GenericViewSet, ListModelMixin):
    
        queryset = Book.objects.all()
        serializer_class = BookSerializer
    
        filter_backends = [DjangoFilterBackend,]
    
        filter_fields = ['name','author']
    

    路由還是原來的配置。

    這時訪問是要過濾,關鍵字不能寫search了,寫search會把全部都打印出來,要寫具體的字段名,而且后面要過濾的內容是精準匹配

    http://127.0.0.1:8000/books/?name=西游記, 如果像之前直接寫西則匹配不出來
    

    image-20220406225631132

    http://127.0.0.1:8000/books/?name=活著&author=余華  # 這里面是and的關系,name是活著并且author是余華的
    

    image-20220406225714022

    1.3 自定義過濾類

    單獨寫一個類繼承BaseFilterBackend 基類,重寫filter_queryset方法,返回queryset對象

    filter.py

    from rest_framework.filters import BaseFilterBackend
    
    class BookFilter(BaseFilterBackend):
        def filter_queryset(self, request, queryset, view):
            query = request.query_params.get('name')
            if query:
                queryset =  queryset.filter(name__contains=query)
            return queryset
    
    

    views.py

    from rest_framework.viewsets import GenericViewSet
    from rest_framework.mixins import ListModelMixin
    
    from 應用名.models import Book  # 數據庫
    from 應用名.serializer import BookSerializer # 序列化器
    
    
    from 應用名.filter import BookFilter
    
    
    class BookView(GenericViewSet, ListModelMixin):
    
        queryset = Book.objects.all()
        serializer_class = BookSerializer
    
        # filter_backends = [DjangoFilterBackend,]
        filter_backends = [BookFilter,]
    
    

    由于只寫了name字段,所以只能匹配name

    http://127.0.0.1:8000/books/?name=西  # 模糊匹配 ,自己定義的
    

    image-20220407000028763

    image-20220407000129979

    2. 排序

    可以使用DRF內置的OrderingFilter類進行排序。

    views.py

    from rest_framework.viewsets import GenericViewSet
    from rest_framework.mixins import ListModelMixin
    
    from 應用名.models import Book  # 數據庫
    from 應用名.serializer import BookSerializer # 序列化器
    
    
    
    from rest_framework.filters import OrderingFilter
    class BookView(GenericViewSet, ListModelMixin):
    
        queryset = Book.objects.all()
        serializer_class = BookSerializer
        filter_backends = [OrderingFilter,]
    
        ordering_fields=['price']  # 按價格排序
    

    訪問:

    http://127.0.0.1:8000/books/?ordering=price  # 正序
    

    image-20220407232111823

    http://127.0.0.1:8000/books/?ordering=-price  # 倒序, 使用減號(-)為倒序
    

    image-20220407232224688

    可以把過濾和排序放在一塊

    views.py

    from rest_framework.viewsets import GenericViewSet
    from rest_framework.mixins import ListModelMixin
    
    from 應用名.models import Book  # 數據庫
    from 應用名.serializer import BookSerializer # 序列化器
    
    from rest_framework.filters import SearchFilter
    
    
    from rest_framework.filters import OrderingFilter
    
    class BookView(GenericViewSet, ListModelMixin):
    
        queryset = Book.objects.all()
        serializer_class = BookSerializer
    
        filter_backends = [SearchFilter,OrderingFilter,]
    	# 排序
        ordering_fields=['price', 'id']
        # 過濾namt或author
        search_fields = ['name','author']
    

    image-20220407233105436

    image-20220407233125935

    3. 分頁

    接口中也只有查詢所有用到了分頁。

    默認的三種分頁方法

    3.1 方法一:基本分頁PageNumberPagination

    基本分頁,按照頁碼數,每頁顯示多少條

    單獨創建一個文件專門用來分頁:page.py

    # 繼承 PageNumberPagination,然后重寫四個屬性
    from rest_framework.pagination import PageNumberPagination
    class  commPageNumberPagination(PageNumberPagination):
        page_size= 3 # 默認每頁顯示的條數
        page_query_param = 'page' # 查詢條件為page, 如:?page=3
        page_size_query_param ='size' # 每頁顯示的條數的查詢條件 ?page=3&size=9 查詢第三頁,第三頁顯示9條
        max_page_size = 5 # 每頁最多顯示幾條, ?page=3&size=9,最終還是顯示5條
    
    

    views.py

    from rest_framework.viewsets import GenericViewSet
    from rest_framework.mixins import ListModelMixin
    
    from 應用名.models import Book  # 數據庫
    from 應用名.serializer import BookSerializer # 序列化器
    
    from 應用名.page import commPageNumberPagination
    class BookView(GenericViewSet, ListModelMixin):
    
        queryset = Book.objects.all()
        serializer_class = BookSerializer
        pagination_class = commPageNumberPagination
    
    

    路由不變,默認訪問:

    http://127.0.0.1:8000/books/

    可看到默認顯示3條,

    image-20220408011717608

    訪問第一頁,每頁顯示9條。

    http://127.0.0.1:8000/books/?page=1&size=9

    由于設置了最多顯示5條,所以雖然設置了要顯示9條,但最多也是顯示5條

    image-20220408011829555

    3.2 方法二:偏移分頁 LimitOffsetPagination

    page.py

    from rest_framework.pagination import LimitOffsetPagination
    
    class commLimitOffsetPagination(LimitOffsetPagination):
        default_limit = 3  # 默認一頁獲取條數 3  條
        limit_query_param = 'limit'  # ?limit=3  獲取三條,如果不傳,就用上面的默認兩條
        offset_query_param = 'offset'  #  ?limit=3&offset=2  從第2條開始,獲取3條    ?offset=3:從第三條開始,獲取2條
        max_limit = 4 # 最大顯示條數 4 條
    
    

    views.py

    from rest_framework.viewsets import GenericViewSet
    from rest_framework.mixins import ListModelMixin
    
    from 應用名.models import Book  # 數據庫
    from 應用名.serializer import BookSerializer # 序列化器
    
    from 應用名.page import commLimitOffsetPagination
    
    class BookView(GenericViewSet, ListModelMixin):
    
        queryset = Book.objects.all()
        serializer_class = BookSerializer
        
        pagination_class = commLimitOffsetPagination
    
    
    
    

    訪問:

    http://127.0.0.1:8000/books/

    image-20220408012638592

    從第二條開始,每頁顯示三條

    image-20220408012830050

    3.3 方法三 游標分頁 CursorPagination

    page.py

    from rest_framework.pagination import CursorPagination
    
    class commCursorPagination(CursorPagination):
        page_size = 3  # 每頁顯示2條
        cursor_query_param = 'cursor'   # 查詢條件  ?cursor=sdafdase
        ordering = 'id' # 排序規則,使用id排序
    
    

    views.py

    from rest_framework.viewsets import GenericViewSet
    from rest_framework.mixins import ListModelMixin
    
    from 應用名.models import Book  # 數據庫
    from 應用名.serializer import BookSerializer # 序列化器
    from authenticated.page import commCursorPagination
    
    class BookView(GenericViewSet, ListModelMixin):
    
        queryset = Book.objects.all()
        serializer_class = BookSerializer
    
        pagination_class = commCursorPagination
    

    訪問:

    http://127.0.0.1:8000/books/

    image-20220408013435703

    3.4 三種分頁總結

    使用這三種分頁視圖類上,必須繼承GenericAPIView
    
    前面兩種可以從中間位置獲取某一頁,但是游標分頁方式只能上一頁和下一頁
    
    前面兩種在獲取某一頁的時候,都需要從開始過濾到要取的頁面數的數據
    
    游標分頁方式,先排序,內部維護了一個游標,游標只能選擇往前走或往后走,在取某一頁的時候,不需要過濾之前的數據,只能選擇上一頁和下一頁,不能指定某一頁,但是速度快,適合大數據量的分頁,在大數據量和app分頁時,下拉加載下一頁,不需要指定跳轉到第幾頁
    
    
    

    4. 異常處理

    DRF中捕獲了全局異常,在執行三大認證,視圖類的方法時候,如果出了異常,會被全局異常捕獲。

    如果自己要自己處理異常,則需要考慮:統一返回格式,無論是否異常,返回的格式統一 ,記錄日志(好排查)

    異常:
    {code:999,msg:服務器異常,請聯系系統管理員}
    成功:
    {code:100,msg:成功,data:[{},{}]}
      
    

    4.1 自己處理異常

    寫一個視圖函數

    views.py

    from rest_framework.views import APIView
    from rest_framework.response import Response
    class TestView(APIView):
        def get(self,request):
            # 第一:程序出錯
            l=[1,2,3]
            print(l[99])
    
            return Response('ok')
    
    

    配置路由

    urls.py

    from django.contrib import admin
    from django.urls import path,include
    from authenticated import views
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('test/', views.TestView.as_view()),
    ]
    
    

    使用自帶的異常處理時:

    http://127.0.0.1:8000/test/

    image-20220408015711905

    自己處理異常

    第一步:創建一個專門處理的文件,里面寫處理異常的代碼。

    excepotion.py

    from rest_framework.views import exception_handler  # 默認沒有配置,出了異常會走它
    from rest_framework.response import Response
    
    def common_exception_handler(exc, context):
        # 第一步,先執行原來的exception_handler
        # 第一種情況,返回Response對象,這表示已經處理了異常,它只處理APIExcepiton的異常,第二種情況,返回None,表示沒有處理
        res = exception_handler(exc, context)
        if not res:
            # 執行這里就說明res為None,它沒有處理異常
            res = Response(data={'code': 1001, 'msg': str(exc)})
            return res
        return res
    
        # 注意:咱們在這里,可以記錄日志---》只要走到這,說明程序報錯了,記錄日志,以后查日志---》盡量詳細
        # 出錯時間,錯誤原因,哪個視圖類出了錯,什么請求地址,什么請求方式出了錯
        request = context.get('request')  # 這個request是當次請求的request對象
        view = context.get('view')  # 這個viewt是當次執行的視圖類對象
        print('錯誤原因:%s,錯誤視圖類:%s,請求地址:%s,請求方式:%s' % (str(exc), str(view), request.path, request.method))
        return res
      
      
    
        
    ### 以后再出異常,都會走這個函數,后期需要記錄日志,統一了返回格式
    

    第二步:把函數配置在配置文件中settings.py

    REST_FRAMEWORK = {
        'EXCEPTION_HANDLER':  'authenticated.exceptions.common_exception_handler',# 再出異常,會執行這個函數
    }
    # authenticated 為應用名
    

    第三步:測試

    寫視圖類故意有程序錯誤:

    views.py

    from rest_framework.views import APIView
    from rest_framework.response import Response
    class TestView(APIView):
        def get(self,request):
            # 第一:程序出錯
            l=[1,2,3]
            print(l[99])
    
            return Response('ok')
    

    訪問:http://127.0.0.1:8000/test/

    image-20220408021018176

    寫視圖類主動拋異常:

    from rest_framework.views import APIView
    from rest_framework.response import Response
    class TestView(APIView):
        def get(self,request):
    		raise Exception('程序異常,請聯系管理員')
    
            return Response('ok')
    

    image-20220408021337600

    寫視圖類主動拋APIException異常

    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework.exceptions import APIException
    class TestView(APIView):
        def get(self,request):
    		raise APIException('APIException異常')
    
            return Response('ok')
    

    訪問發現這個里面就沒有code,因為這是一個APIException異常,DRF會捕捉到。

    image-20220408021509552

    如果APIException也想自己處理:

    excepotion.py

    from rest_framework.views import exception_handler  # 默認沒有配置,出了異常會走它
    from rest_framework.response import Response
    
    def common_exception_handler(exc, context):
        # 第一步,先執行原來的exception_handler
        # 第一種情況,返回Response對象,這表示已經處理了異常,它只處理APIExcepiton的異常,第二種情況,返回None,表示沒有處理
        res = exception_handler(exc, context)
        if not res:
            # 執行這里就說明res為None,它沒有處理異常
            res = Response(data={'code': 1001, 'msg': str(exc)})
            return res
            # 如果執行到這說明res內容,返回的是Response對象, res.data.get('detail', '請聯系管理員')表示如果detail里面有內容,則用途detail里面的,沒有則使用'請聯系管理員'
        res = Response(data={'code': 1002, 'msg': res.data.get('detail', '請聯系管理員')})
        return res
    

    image-20220408022515944

    沒用設置則顯示自己處理時寫的:

    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework.exceptions import APIException
    class TestView(APIView):
        def get(self,request):
    		raise APIException()
    
            return Response('ok')
    

    image-20220408022552389

    4.2 出現異常記錄日志

    excepotion.py

    from rest_framework.views import exception_handler  # 默認沒有配置,出了異常會走它
    from rest_framework.response import Response
    
    
    def common_exception_handler(exc, context):
        # 第一步,先執行原來的exception_handler
        # 第一種情況,返回Response對象,這表示已經處理了異常,它只處理APIExcepiton的異常,第二種情況,返回None,表示沒有處理
        res = exception_handler(exc, context)
        if not res:
            # 執行這里就說明res為None,它沒有處理異常
            res = Response(data={'code': 1001, 'msg': str(exc)})
            return res
        
        # 如果執行到這說明res內容,返回的是Response對象, res.data.get('detail', '請聯系管理員')表示如果detail里面有內容,則用途detail里面的,沒有則使用'請聯系管理員'
        res = Response(data={'code': 1002, 'msg': res.data.get('detail', '請聯系管理員')})
        
        # 記錄日志
        request = context.get('request')  # 這個request是當次請求的request對象
        view = context.get('view')  # 這個viewt是當次執行的視圖類對象
        print('錯誤原因:%s,錯誤視圖類:%s,請求地址:%s,請求方式:%s' % (str(exc), str(view), request.path, request.method))
        return res
    
    

    訪問時后臺會把錯誤信息打印出來:

    錯誤原因:服務器出現了錯誤。,錯誤視圖類:<authenticated.views.TestView object at 0x000002A296C92560>,請求地址:/test/,請求方式:GET
    

    這里是吧錯誤信息打印出來了,正確做法是把它記錄到一個日志文件里面,具體的可以使用python的日志模塊logging

    本文摘自 :https://www.cnblogs.com/

    開通會員,享受整站包年服務
    国产呦精品一区二区三区网站|久久www免费人咸|精品无码人妻一区二区|久99久热只有精品国产15|中文字幕亚洲无线码