66、权限、认证源码 、 django中的翻译函数 、 排序 、过滤 、 分页

142 阅读8分钟

权限源码分析

APIView执行流程

1.有了drf,后期都写CBV,都是继承APIView及其子类

2.执行流程
	1.入口:path('books/', BookView.as_view())  -->请求来了执行:BookView.as_view()(request,)
  2.自己写的BookView类里没有as_view绑定的方法--->父类APIView里有as_view绑定的方法
  3.请求来了,执行 :(View类的as_view中的view,只是去掉了csrf的认证)(request)
  4.执行父类APIView中有dispatch方法
                                                
3.总结
  	1.以后只要继承APIView的所有视图类的方法,都没有csrf的校验了
    2.以后只要继承APIView的所有视图类的方法 中的request是新的request了
    3.在执行视图类的方法之前,执行了三大认证(认证,权限,频率)
    4.期间除了各种错误,都会被异常捕获,统一处理 

分析

1.鼠标点进了APIView的dispatch()

def dispatch(self, request, *args, **kwargs):
        request = self.initialize_request(request, *args, **kwargs)  # 变为新的request
        self.request = request  

        try:
            self.initial(request, *args, **kwargs)  # 进行了三大验证
            if request.method.lower() in self.http_method_names:
                handler = getattr(self, request.method.lower(),
                                  self.http_method_not_allowed)
            else:
                handler = self.http_method_not_allowed

            response = handler(request, *args, **kwargs)

        except Exception as exc:
            response = self.handle_exception(exc)

        self.response = self.finalize_response(request, response, *args, **kwargs)
        return self.response

鼠标点进:self.initial(request, *args, **kwargs)-->是APIView的initial()

    def initial(self, request, *args, **kwargs):
				...
        self.perform_authentication(request)  # 认证
        self.check_permissions(request)  # 权限
        self.check_throttles(request) # 频率

权限源码

鼠标点进check_permissions(request) -->是APIView的check_permissions(self, request)

    def check_permissions(self, request):
        for permission in self.get_permissions():
            if not permission.has_permission(request, self):# 这里的self是视图类对象(BookView,PublisViwe)
                self.permission_denied(
                    request,
                    message=getattr(permission, 'message', None),
                    code=getattr(permission, 'code', None)
                )

ps:
	1.self.get_permissions():是我们配置在视图类上的permission_classes列表中的权限类,一个个的对象
  2.permission.has_permission(request, self):对象调方法,这里的has_permission(request, self),是我们自己写的权限类中has_permission(self, request, view)方法
  3.if not permission.has_permission(request, self):表示权限没有通过
  4.message=getattr(permission, 'message', None)-->我们在自己写的权限类中,获取错误的信息:self.message

鼠标点进:get_permissions()--->是APIView的get_permissions(self)

    def get_permissions(self):
        return [permission() for permission in self.permission_classes]   # self是视图类对象eg(BookView)
    
ps:1.[permission() for permission in self.permission_classes]:是一个列表生成式
	 2.self.permission_classes:permission_classes-->在视图类里面写的权限列表,eg:[AdminPermission],可以有多个
   3.permission(),是自己写的权限类的对象
   4.[permission() for permission in self.permission_classes]:里面是一个个的我们自己写的权限对象

总结

1.写的权限类,一定要写一个方法has_permission,返回TrueFalse
2.配置再视图类上

认证源码

鼠标点进:self.perform_authentication(request)--->APView中的initial()

    def perform_authentication(self, request):
        request.user
      
ps:request.user:是rest_framework.request.Request中的user方法(被伪装成属性)

鼠标点进Request中的user方法

    @property
    def user(self):
        if not hasattr(self, '_user'):   # 验证前没有request.user
            with wrap_attributeerrors():
                self._authenticate()  # request.authenticate
        return self._user

鼠标点击:self._authenticate()--->是Request中的_authenticate()

    def _authenticate(self):
        for authenticator in self.authenticators:  # 
            try:
                user_auth_tuple = authenticator.authenticate(self)  
            except exceptions.APIException:  # 
                self._not_authenticated() 
                raise  # 抛异常,我们自己导入的AuthenticationFaile,继承了APIException

            if user_auth_tuple is not None:   # 认证通过
                self._authenticator = authenticator
                self.user, self.auth = user_auth_tuple   # self是Request对象 
                return
        self._not_authenticated()
        
ps:
  1.self.authenticators:是我们自己写的认证类列表--->列表推导式--->[LoginAuth(),]
  2.authenticator.authenticate(self):对象authenticator调用我们自己写的认证类的authenticate()方法,返回(user,token)

鼠标点进:self.authenticators,进入到Request初始化中的:self.authenticators = (forced_auth,)-->self.authenticators = authenticators or ()

"""
在APIView中的dispatch产生新的request中,request = self.initialize_request(request, *args, **kwargs),initialize_request()返回了Request(request,...,authenticators=self.get_authenticators(),...),
"""

鼠标点进:get_authenticators()--->APIView中的get_authenticators()

    def get_authenticators(self):
        return [auth() for auth in self.authentication_classes]

总结

1.认证类,必须写一个方法authenticate
2.如果认证通过,可以返回None,也可也返回两个值,但是第一个值,尽量是当前登录用户,第二个值一般放token
3.认证失败,抛异常AuthenticationFailed,继承了APIException,他能捕获

django中的翻译函数

1.在rest_framework中的exception,里面有各种报错的类
2.eg:
class MethodNotAllowed(APIException):
    status_code = status.HTTP_405_METHOD_NOT_ALLOWED
    default_detail = _('Method "{method}" not allowed.')
    default_code = 'method_not_allowed'
3.翻译函数:_
	from django.utils.translation import gettext_lazy as _
_('hello')

排序

前期准备

models.py

from django.db import models

class Book(models.Model):
    name = models.CharField(max_length=32)
    price = models.IntegerField()

serializer.py

from rest_framework import serializers
from .models import Book

class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = '__all__'

views.py

from rest_framework.mixins import ListModelMixin
from  rest_framework.viewsets import GenericViewSet
from .serializer import BookSerializer
from .models import Book

class BookView(GenericViewSet,ListModelMixin):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

urls.py

from django.contrib import admin
from django.urls import path,include
from app01.views import BookView
from rest_framework.routers import SimpleRouter
router = SimpleRouter()
router.register('books',BookView,'books')

urlpatterns = [
    path('admin/', admin.site.urls),
path('api/v1/', include(router.urls)),

]

排序类

1.在restful规范中--请求地址中带过滤条件,?后面写排序、过滤条件
2.对于列表数据,REST framework提供了OrderingFilter过滤器来帮助我们快速指明数据按照指定字段进行排序。
3.排序:必须是继承GenericAPIView及其子类才能用
4.drf提供了要给排序类,用他的就可以了,如果继承了APIViwe,不能这么用
5.使用排序类,在视图函数下写:filter_backends = [OrderingFilter]  # 排序类

案例

from rest_framework.filters import OrderingFilter

class BookView(GenericViewSet,ListModelMixin):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    filter_backends = [OrderingFilter]  # 排序类
    ordering_fields = ['price','id']  # 可以排序的字段

需求:按价格排序

http://127.0.0.1:8000/api/v1/books/?ordering=price   # 升序
http://127.0.0.1:8000/api/v1/books/?ordering=-price  # 倒序

需求:按id排序

http://127.0.0.1:8000/api/v1/books/?ordering=id
http://127.0.0.1:8000/api/v1/books/?ordering=-id

需求:先按价格排序,价格一样,再按id排序-->逗号隔开

http://127.0.0.1:8000/api/v1/books/?ordering=price,id
http://127.0.0.1:8000/api/v1/books/?ordering=-price,id

过滤

1.过滤三种方式
	1.内置过滤类 	
  2.第三方过滤类
  3.自定义过滤类:继承filters.BaseFilterBackend
2.过滤:必须是继承GenericAPIView及其子类才能用
3.drf提供了要给排序类,用他的就可以了,如果继承了APIViwe,不能这么用
4.过滤和排序可以一起使用

内置过滤类

可以模糊匹配

1.导模块:from rest_framework.filters import SearchFilter
2.视图函数属性:
		filter_backends = [SearchFilter]
    search_fields = ['字段'] 
3.使用方式固定:?search = 11

需求:查找书名中含红的书

from rest_framework.filters import SearchFilter
class BookView(GenericViewSet,ListModelMixin):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    filter_backends = [SearchFilter]
    search_fields = ['name'] 
搜索:http://127.0.0.1:8000/api/v1/books/?search=红

需求:查找价格为99的书

from rest_framework.filters import SearchFilter
class BookView(GenericViewSet,ListModelMixin):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    filter_backends = [SearchFilter]
    search_fields=['price']  
搜索:http://127.0.0.1:8000/api/v1/books/?search=99

需求:查找书名或价格含有34的书

from rest_framework.filters import SearchFilter
class BookView(GenericViewSet,ListModelMixin):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    filter_backends = [SearchFilter]
    search_fields = ['name','price']  
搜索:http://127.0.0.1:8000/api/v1/books/?search=34

第三方过滤类

可以精准匹配

1.下载:pip3 install django-filter
2.在配置文件注册应用
INSTALLED_APPS = [
  ...
  'django_filters',  # 需要注册应用,
]
3.使用:from django_filters.rest_framework import DjangoFilterBackend
4.类面的属性
	  filter_backends = [DjangoFilterBackend]
    filterset_fields=['字段'] 
5.在浏览器中,就可以通过kv筛选

ps:filter_backends是GenericAPIView的属性

需求:查找图书书名为:简爱

from django_filters.rest_framework import DjangoFilterBackend

class BookView(GenericViewSet,ListModelMixin):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    filter_backends = [DjangoFilterBackend]
    filterset_fields=['name'] 
http://127.0.0.1:8000/api/v1/books/?name=简爱

需求:查找图书为简爱,价格为234的书

from django_filters.rest_framework import DjangoFilterBackend

class BookView(GenericViewSet,ListModelMixin):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    filter_backends = [DjangoFilterBackend]
    filterset_fields=['name','price']  # 按名字和价格精准匹配
http://127.0.0.1:8000/api/v1/books/?name=简爱&price=234

自定义过滤类

1.自定义的过滤类
	1.继承filters.BaseFilterBackend类
  2.必须重写filter_queryset方法
  	ps:其中的形参是QuerySet对象
    		返回的数据就是过滤后的数据
2.在视图类写属性
	filter_backends = [自定义的过滤类类名]

需求:筛选出书名为红楼梦,或者价格为66的书


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

class MyFilter(filters.BaseFilterBackend):
    def filter_queryset(self, request, queryset, view):
        print(queryset)   # 书的QuerySet对象<QuerySet [<Book: Book object (1)>,,,...]>
        # 返回的数据就是过滤后的数据
        price = request.query_params.get('price')
        name = request.query_params.get('name')
        queryset = queryset.filter(Q(name = name)|Q(price=price))
        return queryset

views.py

from .filter import MyFilter
class BookView(GenericViewSet,ListModelMixin):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    filter_backends = [MyFilter]

增加需求:筛选出书名为红楼梦,或者价格为66的书后,按id排序

from .filter import MyFilter
from rest_framework.filters import OrderingFilter

class BookView(GenericViewSet,ListModelMixin):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    filter_backends = [MyFilter,OrderingFilter]
    ordering_fields=['id']

http://127.0.0.1:8000/api/v1/books/?name=%E8%BF%BD%E9%A3%8E&price=234&ordering=-id

分页

1.查询所有的接口,需要有分页功能

2.分页的展现形式
	web:下一页点解
	app,小程序:下滑下一页
	接口都一样,要支持分页
    
3.drf提供给咱们,三种分页方式
	1.基本分页:PageNumberPagination
	2.偏移分页
	3.游标分页
  
3.视图类中只能使用一种方式分页,不要写在列表中
	eg:pagination_class = MyPageNumberPagination 

基本分页

1.写自定义的分页类
	from rest_framework.pagination import PageNumberPagination
	1.继承PageNumberPagination
  2.重写类属性: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  # 每页最多显示多少条  默认为None

案例

page.py


from rest_framework.pagination import PageNumberPagination

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  # 每页最多显示多少条  默认为None

views.py

from .page import MyPageNumberPagination

class BookView(GenericViewSet,ListModelMixin):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    pagination_class = MyPageNumberPagination  # 只能按一种方式分页,不要放到列表中了

http://127.0.0.1:8000/api/v1/books/?page=3&page_size=3
http://127.0.0.1:8000/api/v1/books/?page=4

偏移分页

1.写自定义的分页类
	from rest_framework.pagination import LimitOffsetPagination
	1.继承LimitOffsetPagination
  2.重写类属性:4个
      default_limit = 2   # 每页显示多少条
      limit_query_param = 'limit'  # limit=3  这一页取3条
      offset_query_param = 'offset'  # 偏移量是多少  offset=3&limit=2  从第3条开始,拿2条
      max_limit = 5  # 最多取5条

案例

page.py


from rest_framework.pagination import LimitOffsetPagination
class MyLimitOffsetPagination(LimitOffsetPagination):
    # 重写类属性 :4个
    default_limit = 2   # 每页显示多少条
    limit_query_param = 'limit'  # limit=3  这一页取3条
    offset_query_param = 'offset'  # 偏移量是多少  offset=3&limit=2  从第3条开始,拿2条
    max_limit = 5  # 最多取5条

views.py


from .page import MyLimitOffsetPagination
class BookView(GenericViewSet,ListModelMixin):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    pagination_class = MyLimitOffsetPagination  
    # http://127.0.0.1:8000/api/v1/books/?limit=3&offset=6

游标分页

1.写自定义的分页类
	from rest_framework.pagination import CursorPagination
	1.继承CursorPagination
  2.重写类属性:3个
    cursor_query_param = 'cursor'  # 查询参数,其实用不到
    page_size = 2  # 每页显示条数
    ordering = 'id'  # 必须是要分页的数据表中的字段,一般按id来
2.游标分页,只能上一页和下一页,不能直接跳到某一页,但是这个的速度快-->app上用它多

案例


from rest_framework.pagination import CursorPagination
class MyCursorPagination(CursorPagination):
    # 重写类属性:3个
    cursor_query_param = 'cursor'  # 查询参数,其实用不到
    page_size = 2  # 每页显示条数
    ordering = 'id'  # 必须是要分页的数据表中的字段,一般按id来
from .page import MyCursorPagination

class BookView(GenericViewSet,ListModelMixin):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    pagination_class = MyCursorPagination

作业

写查询所有出版社接口

1.可以按名字或城市  模糊 匹配
2.如果城市一样,可以按id倒序排列
from rest_framework.viewsets import GenericViewSet
from app01.models import Publish
from .serializer import PublishSerializer
from rest_framework.mixins import ListModelMixin
from rest_framework.response import Response
# from .filter import Myfilter
from rest_framework.filters import SearchFilter
from rest_framework.filters import OrderingFilter

class PublishView(GenericViewSet,ListModelMixin):
    queryset = Publish.objects.all()
    serializer_class = PublishSerializer
    filter_backends = [SearchFilter,OrderingFilter]
    search_fields = ['name','addr']
    ordering_fields = ['id']
    # http://127.0.0.1:8000/app02/api/v1/publish/?search=%E5%8C%97%E4%BA%AC&ordering=-id

上述接口,带分页功能


from rest_framework.pagination import PageNumberPagination
class MyPage(PageNumberPagination):
    page_size = 2
    page_query_param = 'page'
    page_size_query_param = 2
    max_page_size = 4
    
from rest_framework.viewsets import GenericViewSet
from app01.models import Publish
from .serializer import PublishSerializer
from rest_framework.mixins import ListModelMixin
from rest_framework.response import Response
from rest_framework.filters import SearchFilter
from rest_framework.filters import OrderingFilter
from .page import MyPage

class PublishView(GenericViewSet,ListModelMixin):
    queryset = Publish.objects.all()
    serializer_class = PublishSerializer
    filter_backends = [SearchFilter,OrderingFilter]
    search_fields = ['name','addr']
    ordering_fields = ['id']
    pagination_class = MyPage

补充

1.__开头,隐藏
		内部使用self.__属性/方法
  	外部使用:person._类名__字段名
2.别人,公司内部,不太使用 __
	约定俗称,类内部使用 _开头,本意就是隐藏,不给外部使用