當(dāng)前位置:首頁 > IT技術(shù) > 編程語言 > 正文

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

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

1. 過濾

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

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

1.1 內(nèi)置過濾類

views.py

from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import ListModelMixin

from 應(yīng)用名.models import Book  # 數(shù)據(jù)庫
from 應(yīng)用名.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

第二步在配置里注冊(cè)settings.py

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

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

from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import ListModelMixin

from 應(yīng)用名.models import Book  # 數(shù)據(jù)庫
from 應(yīng)用名.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']

路由還是原來的配置。

這時(shí)訪問是要過濾,關(guān)鍵字不能寫search了,寫search會(huì)把全部都打印出來,要寫具體的字段名,而且后面要過濾的內(nèi)容是精準(zhǔn)匹配

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

image-20220406225631132

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

image-20220406225714022

1.3 自定義過濾類

單獨(dú)寫一個(gè)類繼承BaseFilterBackend 基類,重寫filter_queryset方法,返回queryset對(duì)象

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 應(yīng)用名.models import Book  # 數(shù)據(jù)庫
from 應(yīng)用名.serializer import BookSerializer # 序列化器


from 應(yīng)用名.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內(nèi)置的OrderingFilter類進(jìn)行排序。

views.py

from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import ListModelMixin

from 應(yīng)用名.models import Book  # 數(shù)據(jù)庫
from 應(yīng)用名.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']  # 按價(jià)格排序

訪問:

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

image-20220407232111823

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

image-20220407232224688

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

views.py

from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import ListModelMixin

from 應(yīng)用名.models import Book  # 數(shù)據(jù)庫
from 應(yīng)用名.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. 分頁

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

默認(rèn)的三種分頁方法

3.1 方法一:基本分頁P(yáng)ageNumberPagination

基本分頁,按照頁碼數(shù),每頁顯示多少條

單獨(dú)創(chuàng)建一個(gè)文件專門用來分頁:page.py

# 繼承 PageNumberPagination,然后重寫四個(gè)屬性
from rest_framework.pagination import PageNumberPagination
class  commPageNumberPagination(PageNumberPagination):
    page_size= 3 # 默認(rèn)每頁顯示的條數(shù)
    page_query_param = 'page' # 查詢條件為page, 如:?page=3
    page_size_query_param ='size' # 每頁顯示的條數(shù)的查詢條件 ?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 應(yīng)用名.models import Book  # 數(shù)據(jù)庫
from 應(yīng)用名.serializer import BookSerializer # 序列化器

from 應(yīng)用名.page import commPageNumberPagination
class BookView(GenericViewSet, ListModelMixin):

    queryset = Book.objects.all()
    serializer_class = BookSerializer
    pagination_class = commPageNumberPagination

路由不變,默認(rèn)訪問:

http://127.0.0.1:8000/books/

可看到默認(rèn)顯示3條,

image-20220408011717608

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

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

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

image-20220408011829555

3.2 方法二:偏移分頁 LimitOffsetPagination

page.py

from rest_framework.pagination import LimitOffsetPagination

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

views.py

from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import ListModelMixin

from 應(yīng)用名.models import Book  # 數(shù)據(jù)庫
from 應(yīng)用名.serializer import BookSerializer # 序列化器

from 應(yīng)用名.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 方法三 游標(biāo)分頁 CursorPagination

page.py

from rest_framework.pagination import CursorPagination

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

views.py

from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import ListModelMixin

from 應(yīng)用名.models import Book  # 數(shù)據(jù)庫
from 應(yīng)用名.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 三種分頁總結(jié)

使用這三種分頁視圖類上,必須繼承GenericAPIView

前面兩種可以從中間位置獲取某一頁,但是游標(biāo)分頁方式只能上一頁和下一頁

前面兩種在獲取某一頁的時(shí)候,都需要從開始過濾到要取的頁面數(shù)的數(shù)據(jù)

游標(biāo)分頁方式,先排序,內(nèi)部維護(hù)了一個(gè)游標(biāo),游標(biāo)只能選擇往前走或往后走,在取某一頁的時(shí)候,不需要過濾之前的數(shù)據(jù),只能選擇上一頁和下一頁,不能指定某一頁,但是速度快,適合大數(shù)據(jù)量的分頁,在大數(shù)據(jù)量和app分頁時(shí),下拉加載下一頁,不需要指定跳轉(zhuǎn)到第幾頁


4. 異常處理

DRF中捕獲了全局異常,在執(zhí)行三大認(rèn)證,視圖類的方法時(shí)候,如果出了異常,會(huì)被全局異常捕獲。

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

異常:
{code:999,msg:服務(wù)器異常,請(qǐng)聯(lián)系系統(tǒng)管理員}
成功:
{code:100,msg:成功,data:[{},{}]}
  

4.1 自己處理異常

寫一個(gè)視圖函數(shù)

views.py

from rest_framework.views import APIView
from rest_framework.response import Response
class TestView(APIView):
    def get(self,request):
        # 第一:程序出錯(cuò)
        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()),
]

使用自帶的異常處理時(shí):

http://127.0.0.1:8000/test/

image-20220408015711905

自己處理異常

第一步:創(chuàng)建一個(gè)專門處理的文件,里面寫處理異常的代碼。

excepotion.py

from rest_framework.views import exception_handler  # 默認(rèn)沒有配置,出了異常會(huì)走它
from rest_framework.response import Response

def common_exception_handler(exc, context):
    # 第一步,先執(zhí)行原來的exception_handler
    # 第一種情況,返回Response對(duì)象,這表示已經(jīng)處理了異常,它只處理APIExcepiton的異常,第二種情況,返回None,表示沒有處理
    res = exception_handler(exc, context)
    if not res:
        # 執(zhí)行這里就說明res為None,它沒有處理異常
        res = Response(data={'code': 1001, 'msg': str(exc)})
        return res
    return res

    # 注意:咱們?cè)谶@里,可以記錄日志---》只要走到這,說明程序報(bào)錯(cuò)了,記錄日志,以后查日志---》盡量詳細(xì)
    # 出錯(cuò)時(shí)間,錯(cuò)誤原因,哪個(gè)視圖類出了錯(cuò),什么請(qǐng)求地址,什么請(qǐng)求方式出了錯(cuò)
    request = context.get('request')  # 這個(gè)request是當(dāng)次請(qǐng)求的request對(duì)象
    view = context.get('view')  # 這個(gè)viewt是當(dāng)次執(zhí)行的視圖類對(duì)象
    print('錯(cuò)誤原因:%s,錯(cuò)誤視圖類:%s,請(qǐng)求地址:%s,請(qǐng)求方式:%s' % (str(exc), str(view), request.path, request.method))
    return res
  
  

    
### 以后再出異常,都會(huì)走這個(gè)函數(shù),后期需要記錄日志,統(tǒng)一了返回格式

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

REST_FRAMEWORK = {
    'EXCEPTION_HANDLER':  'authenticated.exceptions.common_exception_handler',# 再出異常,會(huì)執(zhí)行這個(gè)函數(shù)
}
# authenticated 為應(yīng)用名

第三步:測(cè)試

寫視圖類故意有程序錯(cuò)誤:

views.py

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

        return Response('ok')

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

image-20220408021018176

寫視圖類主動(dòng)拋異常:

from rest_framework.views import APIView
from rest_framework.response import Response
class TestView(APIView):
    def get(self,request):
		raise Exception('程序異常,請(qǐng)聯(lián)系管理員')

        return Response('ok')

image-20220408021337600

寫視圖類主動(dòng)拋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')

訪問發(fā)現(xiàn)這個(gè)里面就沒有code,因?yàn)檫@是一個(gè)APIException異常,DRF會(huì)捕捉到。

image-20220408021509552

如果APIException也想自己處理:

excepotion.py

from rest_framework.views import exception_handler  # 默認(rèn)沒有配置,出了異常會(huì)走它
from rest_framework.response import Response

def common_exception_handler(exc, context):
    # 第一步,先執(zhí)行原來的exception_handler
    # 第一種情況,返回Response對(duì)象,這表示已經(jīng)處理了異常,它只處理APIExcepiton的異常,第二種情況,返回None,表示沒有處理
    res = exception_handler(exc, context)
    if not res:
        # 執(zhí)行這里就說明res為None,它沒有處理異常
        res = Response(data={'code': 1001, 'msg': str(exc)})
        return res
        # 如果執(zhí)行到這說明res內(nèi)容,返回的是Response對(duì)象, res.data.get('detail', '請(qǐng)聯(lián)系管理員')表示如果detail里面有內(nèi)容,則用途detail里面的,沒有則使用'請(qǐng)聯(lián)系管理員'
    res = Response(data={'code': 1002, 'msg': res.data.get('detail', '請(qǐng)聯(lián)系管理員')})
    return res

image-20220408022515944

沒用設(shè)置則顯示自己處理時(shí)寫的:

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 出現(xiàn)異常記錄日志

excepotion.py

from rest_framework.views import exception_handler  # 默認(rèn)沒有配置,出了異常會(huì)走它
from rest_framework.response import Response


def common_exception_handler(exc, context):
    # 第一步,先執(zhí)行原來的exception_handler
    # 第一種情況,返回Response對(duì)象,這表示已經(jīng)處理了異常,它只處理APIExcepiton的異常,第二種情況,返回None,表示沒有處理
    res = exception_handler(exc, context)
    if not res:
        # 執(zhí)行這里就說明res為None,它沒有處理異常
        res = Response(data={'code': 1001, 'msg': str(exc)})
        return res
    
    # 如果執(zhí)行到這說明res內(nèi)容,返回的是Response對(duì)象, res.data.get('detail', '請(qǐng)聯(lián)系管理員')表示如果detail里面有內(nèi)容,則用途detail里面的,沒有則使用'請(qǐng)聯(lián)系管理員'
    res = Response(data={'code': 1002, 'msg': res.data.get('detail', '請(qǐng)聯(lián)系管理員')})
    
    # 記錄日志
    request = context.get('request')  # 這個(gè)request是當(dāng)次請(qǐng)求的request對(duì)象
    view = context.get('view')  # 這個(gè)viewt是當(dāng)次執(zhí)行的視圖類對(duì)象
    print('錯(cuò)誤原因:%s,錯(cuò)誤視圖類:%s,請(qǐng)求地址:%s,請(qǐng)求方式:%s' % (str(exc), str(view), request.path, request.method))
    return res

訪問時(shí)后臺(tái)會(huì)把錯(cuò)誤信息打印出來:

錯(cuò)誤原因:服務(wù)器出現(xiàn)了錯(cuò)誤。,錯(cuò)誤視圖類:<authenticated.views.TestView object at 0x000002A296C92560>,請(qǐng)求地址:/test/,請(qǐng)求方式:GET

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

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

開通會(huì)員,享受整站包年服務(wù)立即開通 >