過(guò)濾是涉及到查詢數(shù)據(jù)的接口才需要過(guò)濾功能
公司主營(yíng)業(yè)務(wù):網(wǎng)站制作、網(wǎng)站建設(shè)、移動(dòng)網(wǎng)站開(kāi)發(fā)等業(yè)務(wù)。幫助企業(yè)客戶真正實(shí)現(xiàn)互聯(lián)網(wǎng)宣傳,提高企業(yè)的競(jìng)爭(zhēng)能力。創(chuàng)新互聯(lián)是一支青春激揚(yáng)、勤奮敬業(yè)、活力青春激揚(yáng)、勤奮敬業(yè)、活力澎湃、和諧高效的團(tuán)隊(duì)。公司秉承以“開(kāi)放、自由、嚴(yán)謹(jǐn)、自律”為核心的企業(yè)文化,感謝他們對(duì)我們的高要求,感謝他們從不同領(lǐng)域給我們帶來(lái)的挑戰(zhàn),讓我們激情的團(tuán)隊(duì)有機(jī)會(huì)用頭腦與智慧不斷的給客戶帶來(lái)驚喜。創(chuàng)新互聯(lián)推出正定免費(fèi)做網(wǎng)站回饋大家。
DRF 中使用的過(guò)濾方式:
使用模塊: from rest_framework.filters import SearchFilter
在視圖層中使用內(nèi)置過(guò)濾類(lèi)
前提:需要使用 GenericAPIView
類(lèi)中的filter_backends
屬性,所以視圖類(lèi)得繼承 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
步驟:
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é):
search
1、安裝: pip install django-filter
2、使用模塊: from django_filters.rest_framework import DjangoFilterBackend
3、在項(xiàng)目配置文件 settings.py 中注冊(cè)下載的 app
4、第三方過(guò)濾類(lèi)在filter_backends
字段中寫(xiě),filter_fields
字段指定過(guò)濾的字段
INSTALLED_APPS = [
...
'django_filters', # 需要注冊(cè)應(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
字段中寫(xiě),filter_fields
字段指定過(guò)濾的字段GenericAPIView
才能使用&
來(lái)表示和的關(guān)系外鍵字段怎么查?
1、新建一個(gè)過(guò)濾文件,寫(xiě)一個(gè)類(lèi)繼承BaseFilterBackend
,重寫(xiě)filter_queryset(self, request, queryset, view)
方法,返回queryset對(duì)象,qs對(duì)象是過(guò)濾后的
2、視圖層使用,只需要指定filter_backend
屬性為自定義類(lèi)列表
3、查詢過(guò)濾,支持模糊查詢(自己定制過(guò)濾方式),在filter_queryset
方法自定義過(guò)濾規(guī)則
自定義過(guò)濾類(lèi)的書(shū)寫(xiě):
from rest_framework.filters import BaseFilterBackend
from django.db.models import Q
class Myfilter(BaseFilterBackend):
def filter_queryset(self, request, queryset, view):
# 獲取過(guò)濾參數(shù)
qs_name = request.query_params.get('name')
qs_price = request.query_params.get('price')
# title__contains:精確大小寫(xiě)查詢,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
視圖類(lèi):
from app01.filter import Myfilter
class BookViewSet(ViewSetMixin, ListAPIView):
queryset = models.Book.objects
serializer_class = BookModelSerializer
authentication_classes = [LoginAuth, ]
filter_backends = [Myfilter, ]
我們知道過(guò)濾的前提條件是視圖繼承了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是通過(guò)遍歷該類(lèi)的filter_backends列表的得到的,也就是我們指定的過(guò)濾類(lèi)列表,那么backend就是我們的過(guò)濾類(lèi)
2.通過(guò)實(shí)例化得到對(duì)象來(lái)調(diào)用了類(lèi)內(nèi)的filter_queryset返回了過(guò)濾后的對(duì)象
'''
使用模塊:from rest_framework.filters import OrderingFilter
步驟:
filter_backends
屬性的列表中ordering_fields
指定要排序的字段-
號(hào)代表倒序,且必須使用ordering
指定排序字段視圖類(lèi)書(shū)寫(xiě):
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, ] # 先過(guò)濾后排序
ordering_fields = ['id', 'price']
注意:過(guò)濾可以和排序同時(shí)使用,但是先執(zhí)行過(guò)濾再執(zhí)行排序,提升了代碼的效率(先過(guò)濾后排序),因?yàn)槿绻扰判颍敲磾?shù)據(jù)庫(kù)的數(shù)量龐大的話,直接操作了整個(gè)數(shù)據(jù)庫(kù),消耗資源,過(guò)濾完成后排序只是針對(duì)一小部分?jǐn)?shù)據(jù)
分頁(yè)只在查詢所有接口中使用
導(dǎo)入分頁(yè)類(lèi): from rest_framework.pagination import PageNumberPagination,LimitOffsetPagination,CursorPagination
DRF 中分頁(yè)的三種方式:
步驟:
自定義類(lèi),繼承PageNumberPagination
,重寫(xiě)四個(gè)類(lèi)屬性
注意: 配置在視圖類(lèi)中,通過(guò)pagination_class
指定,必須繼承GenericAPIView才有
分頁(yè)類(lèi)書(shū)寫(xiě):
from rest_framework.pagination import PageNumberPagination
class BookPagination(PageNumberPagination):
page_size = 3 # 默認(rèn)每頁(yè)顯示2條
page_query_param = 'page' # 查詢條件,eg:page=3
page_size_query_param = 'size' # 查詢條件參數(shù)size=5顯示五條
max_page_size = 10 # 每頁(yè)最大顯示條數(shù)
視圖層類(lèi):
給 pagination_class
屬性賦值分頁(yè)類(lèi)
from app01.page import BookPagination
class BookViewSet(ViewSetMixin, ListAPIView):
queryset = models.Book.objects.all()
serializer_class = BookModelSerializer
authentication_classes = []
filter_backends = [Myfilter, OrderingFilter, ] # 先過(guò)濾后排序
ordering_fields = ['id', 'price']
pagination_class = BookPagination
步驟:
分頁(yè)類(lèi)書(shū)寫(xiě):
from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination
class MyLimitOffset(LimitOffsetPagination):
default_limit = 2 # 默認(rèn)每頁(yè)顯示2條
limit_query_param = 'limit' # ?limit=3,查詢出3條
offset_query_param = 'offset' # 偏移量,?offset=2,從第2條后開(kāi)始
max_limit = 5 # 最大顯示5條
視圖層類(lèi):
給 pagination_class
屬性賦值分頁(yè)類(lèi)
from app01.page import BookPagination, MyLimitOffset
class BookViewSet(ViewSetMixin, ListAPIView):
queryset = models.Book.objects.all()
serializer_class = BookModelSerializer
authentication_classes = []
filter_backends = [Myfilter, OrderingFilter, ] # 先過(guò)濾后排序
ordering_fields = ['id', 'price']
pagination_class = MyLimitOffset
步驟:
分頁(yè)類(lèi)書(shū)寫(xiě):
from rest_framework.pagination import CursorPagination
class MyCursor(CursorPagination):
page_size = 3
cursor_query_param = 'cursor'
ordering = 'id'
視圖層類(lèi):
給 pagination_class
屬性賦值分頁(yè)類(lèi)
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)分頁(yè),不要指定排序規(guī)則,會(huì)報(bào)錯(cuò)
ordering_fields = ['id', 'price']
pagination_class = MyCursor
查詢方式
http://127.0.0.1:8000/books/?cursor=cD02
注意:分頁(yè)類(lèi)內(nèi)指定了排序,視圖內(nèi)不要寫(xiě)排序規(guī)則,不然報(bào)錯(cuò)
之前讀APIView源碼的時(shí)候,捕獲了全局異常,在執(zhí)行三大認(rèn)證,視圖類(lèi)的方法時(shí)候,如果出了異常,會(huì)被全局異常捕獲
以下是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) 方法,如果報(bào)的是已知的錯(cuò)會(huì)返回 Response 對(duì)象,未知錯(cuò)誤返回 None
可以自定義出現(xiàn)異常之后的處理方法和返回?cái)?shù)據(jù)的格式
重寫(xiě)異常處理方法:
from rest_framework.views import exception_handler
from rest_framework.response import Response
def myexception_handler(exc, context):
# 先執(zhí)行原來(lái)的exception_handler幫助我們處理
res = exception_handler(exc, context)
if res:
# res有值代表處理過(guò)了APIException對(duì)象的異常了,返回的數(shù)據(jù)再定制
res = Response(data={'code': 998, 'msg': res.data.get('detail', '服務(wù)器異常,請(qǐng)聯(lián)系系統(tǒng)管理員')})
# res = Response(data={'code': 998, 'msg': '服務(wù)器異常,請(qǐng)聯(lián)系系統(tǒng)管理員'})
# res.data.get從響應(yīng)中獲取原來(lái)的處理詳細(xì)信息
else:
res = Response(data={'code': 999, 'msg': str(exc)})
print(exc) # list index out of range
'''模擬日志處理'''
request = context.get('request') # 當(dāng)次請(qǐng)求的request對(duì)象
view = context.get('view') # 當(dāng)次執(zhí)行的視圖類(lèi)對(duì)象
print('錯(cuò)誤原因:%s,錯(cuò)誤視圖類(lèi):%s,請(qǐng)求地址:%s,請(qǐng)求方式:%s' % (str(exc), str(view), request.path, request.method))
'''結(jié)果:
錯(cuò)誤原因:list index out of range,錯(cuò)誤視圖類(lèi):,請(qǐng)求地址:/test/,請(qǐng)求方式:GET
'''
return res
修改異常的配置路徑:
REST_FRAMEWORK = {
'EXCEPTION_HANDLER': 'app01.myexception.exception_handler' # 再出異常,會(huì)執(zhí)行自己定義的函數(shù)
}
視圖類(lèi)中報(bào)錯(cuò)就會(huì)自動(dòng)觸發(fā)異常處理:
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.exceptions import APIException
# 測(cè)試異常視圖
class Test(APIView):
def get(self,request):
# 1、 其他報(bào)錯(cuò)
# l = [1,2,3]
# print(l[100])
# 2、APIException異常
# raise APIException('APIException errors!')
return Response('success!')