DRF之过滤,排序,分页
一、权限组件源码解读
1.继承了APIView
才有的---》执行流程---》dispatch中----》三大认证
APIView的dispatch
def initial(self, request, *args, **kwargs):self.perform_authentication(request)self.check_permissions(request)self.check_throttles(request)
2 读权限:APIView的方法
self.check_permissions(request)def check_permissions(self, request):# permission_classes = [AdminPermission]# self.get_permissions()我们配置再视图类上permission_classes列表中的认证类,一个个的对象# self.get_permissions() 是 [AdminPermission(),]for permission in self.get_permissions():# 写的权限类,要重写has_permission方法# 猜permission是我们写的权限类的对象# self 是视图类的对象(BookView,PublisViwe)if not permission.has_permission(request, self): # 权限没通过self.permission_denied(request,# self.message 错误文字message=getattr(permission, 'message', None),code=getattr(permission, 'code', None))
3 读 APIView--->self.get_permissions
def get_permissions(self):return [permission() for permission in self.permission_classes]# 翻译:l=[]for permission in self.permission_classes:l.append(permission())return l
记住:
- 写的权限类,一定要写一个方法has_permission,返回True或False
- 配置再视图类上
4.补充
视图类的方法,必须返回 4件套或drf的Response
return和raise完全不一样
视图类继承了
视图类:ViewSetMixin,ListModelMixin----》查询所有好多代码不用写----》可以自动生成路由router.register('book',BookView,'book')/book/--->get请求过来被映射了 list---》执行视图类中的list----》BookView中写了get方法,根本不会执行自动生成路由
二、认证源码分析
继承了APIView,才有的---》执行流程---》dispatch中----》三大认证
1 APIView的dispatch
self.initial(request, *args, **kwargs)def initial(self, request, *args, **kwargs):self.perform_authentication(request)self.check_permissions(request)self.check_throttles(request)
2 self.perform_authentication(request)
def perform_authentication(self, request):request.user # 这是个方法,包装成了数据属性
3 Request类的user
@propertydef user(self):if not hasattr(self, '_user'):with wrap_attributeerrors():self._authenticate()return self._user
4 Request类的self._authenticate()
def _authenticate(self):# self.authenticators 就是你写的认证类列表---》列表推导式---》[LoginAuth(),]for authenticator in self.authenticators:try:user_auth_tuple = authenticator.authenticate(self)except exceptions.APIException:# 抛了异常被捕获了if user_auth_tuple is not None:# 如果返回了两个值,就会执行这句话# self是Request的对象self.user, self.auth = user_auth_tuplereturnself._not_authenticated()
5 Request类初始化
APIView---》dispathc的前面
总结:
1 认证类,必须写一个方法authenticate2 如果认证通过,可以返回None,也可也返回两个值,但是第一个值,尽量是当前登录用户,第二个值一般放token3 认证失败,抛异常AuthenticationFailed,继承了APIException,他能捕获
三、django中的翻译函数
只要做了国际化,会自动翻译成,当前国家的语言
from django.utils.translation import gettext_lazy as _
_('hello')
四、过滤
restful规范中
-请求地址中带过滤条件
带过滤的接口只有:查询所有
1.内置过滤类
from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import ListModelMixin
from .models import Book
from rest_framework.filters import SearchFilter# 内置过滤类
class BookView(GenericViewSet, ListModelMixin):queryset = Book.objects.all()serializer_class = BookSerializer
# 支持这种搜索
# http://127.0.0.1:8000/api/v1/books/?search=红
# search_fields = ['name']
# http://127.0.0.1:8000/api/v1/books/?search=11 只要name或price中带11都能搜出来filter_backends = [SearchFilter]search_fields = ['name']
2.第三方过滤类
from django_filters.rest_framework import DjangoFilterBackend第三方过滤类
class BookView(GenericViewSet, ListModelMixin):queryset = Book.objects.all()serializer_class = BookSerializer# 按名字和价格精准匹配# http://127.0.0.1:8000/api/v1/books/?name=红楼梦&price=45filter_backends = [DjangoFilterBackend]filterset_fields = ['name', 'price']
3.自定义过滤类
views:
from .filter import MyFilter自定义过滤类
class BookView(GenericViewSet, ListModelMixin):queryset = Book.objects.all()serializer_class = BookSerializer# 返回的数据,就是过滤后的数据# http://127.0.0.1:8000/api/v1/books/?name=三国演义&price=25 按名字或价格filter_backends = [MyFilter]filterset_fields = ['name', 'price']
filter:
from rest_framework import filters
from django.db.models import Qclass MyFilter(filters.BaseFilterBackend):def filter_queryset(self, request, queryset, view):price = request.query_params.get('price')name = request.query_params.get('name')# 返回的数据,就是过滤后的数据# http://127.0.0.1:8000/api/v1/books/?price=44&name=红楼梦# 按名字或价格queryset = queryset.filter(Q(name=name) | Q(price=price))return queryset
小练习:区间过滤
取出价格在100-200元之间的书
views:
# 自定义过滤价格在一百到两百之间的图书
from .filter import BookFilterSet
from rest_framework.filters import SearchFilterclass BookView(GenericViewSet, ListModelMixin):queryset = Book.objects.all()serializer_class = BookSerializerfilter_backends = [BookFilterSet]search_fields = ['price']
filter:
# 自定义过滤价格在一百到两百之间的图书from rest_framework import filters
from .models import Bookclass BookFilterSet(filters.BaseFilterBackend):def filter_queryset(self, request, queryset, view):price = request.query_params.get('price')min_price = request.query_params.get('min_price')max_price = request.query_params.get('max_price')queryset = queryset.filter(price__gt=min_price, price__lt=max_price)return queryset
过滤和排序可以一起使用:
例:
url:
from django.contrib import admin
from django.urls import path, include
from rest_framework.routers import SimpleRouter
from app01 import viewsrouter = SimpleRouter()
# 图书路由
# router.register('books', views.BookView, 'book')
# 出版社路由
# router.register('publishes', views.PublishView, 'publish')urlpatterns = [path('admin/', admin.site.urls),path('api/v1/', include(router.urls)),
]
views:
from .models import Publish
from .serializer import PublishSerializer
from rest_framework.filters import OrderingFilter
from .page import PublishPageNumberPaginationclass PublishView(GenericViewSet, ListModelMixin):queryset = Publish.objects.all()serializer_class = PublishSerializerfilter_backends = [SearchFilter, OrderingFilter]search_fields = ['title', 'addr']ordering_fields = ['id']pagination_class = PublishPageNumberPagination
page:
class PublishPageNumberPagination(PageNumberPagination):page_size = 1page_size_query_param = 'page_size'page_query_param = 'page'max_page_size = 5
五排序
restful规范中
-请求地址中带过滤条件
排序功能的接口:查询所有
排序
from rest_framework.filters import OrderingFilterclass BookView(GenericViewSet, ListModelMixin):queryset = Book.objects.all()serializer_class = BookSerializer# 排序类# http://127.0.0.1:8000/api/v1/books/?ordering=pricefilter_backends = [OrderingFilter]ordering_fields = ['price']
六 分页
查询所有接口,过滤和排序了,但是实际上,这个接口,都需要有分页功能
-分页的展现形式
web:下一页点解
app,小程序:下滑下一页
-接口都一样,要支持分页
drf提供给咱们,三种分页方式:
1.基本分页(用的较多)
views:
class BookView(GenericViewSet, ListModelMixin):queryset = Book.objects.all()serializer_class = BookSerializer# 基本分页# http://127.0.0.1:8000/api/v1/books/?page=2&page_size=3pagination_class = MyPageNumberPagination # 基本分页
page:
from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination, CursorPagination# 基本分页
class MyPageNumberPagination(PageNumberPagination):# 重新几个类属性 :4个page_size = 2 # 每页的显示条数page_query_param = 'page' # page=4表示第4页page_size_query_param = 'page_size' # page=4&page_size=5 表示查询第4页,每页显示5条max_page_size = 5 # 每页最大显示多少条
2.偏移分页
view:
# 偏移分页# http://127.0.0.1:8000/api/v1/books/?limit=2&offset=5pagination_class = MYLimitOffsetPagination
page:
# 偏移分页
class MYLimitOffsetPagination(LimitOffsetPagination):# 重写几个类属性:4个default_limit = 2 # 每页显示多少条limit_query_param = 'limit' # limit=3 这一页取三条offset_query_param = 'offset' # 偏移量是多少 offset=3max_limit = 5 # 最多取5条
3.游标分页
views:
# 游标分页# http://127.0.0.1:8000/api/v1/books/?ordering=idpagination_class = MyCursorPagination
page:
# 游标分页
class MyCursorPagination(CursorPagination):# 重写三个类属性cursor_query_param = 'cursor' # 查询参数其实用不到page_size = 2 # 每页显示多少条ordering = 'id' # 必须是要分页的数据表中的字段,一般按id来分