权限源码分析
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,返回True或False
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.别人,公司内部,不太使用 __
约定俗称,类内部使用 _开头,本意就是隐藏,不给外部使用