斑马斑马-23-Django REST Framework (DRF)系列教程
学习和使用一个技术、官方文档至关重要。
一、认证Authentication
A:BasicAuthentication This authentication scheme uses HTTP Basic Authentication, signed against a user's username and password. Basic authentication is generally only appropriate for testing. Note: If you use BasicAuthentication in production you must ensure that your API is only available over https. You should also ensure that your API clients will always re-request the username and password at login, and will never store those details to persistent storage.
B:SessionAuthentication This authentication scheme uses Django's default session backend for authentication. Session authentication is appropriate for AJAX clients that are running in the same session context as your website. If successfully authenticated, SessionAuthentication provides the following credentials. * request.user will be a Django User instance. * request.auth will be None. Warning: Always use Django's standard login view when creating login pages. This will ensure your login views are properly protected. CSRF validation in REST framework works slightly differently to standard Django due to the need to support both session and non-session based authentication to the same views. This means that only authenticated requests require CSRF tokens, and anonymous requests may be sent without CSRF tokens. This behaviour is not suitable for login views, which should always have CSRF validation applied.
REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': [ 'rest_framework.authentication.BasicAuthentication', 'rest_framework.authentication.SessionAuthentication', ]}
from APP01.models import BookInfo, HeroInfo from APP01.serializer import BookInfoSerializer, HeroInfoSerializer from rest_framework.authentication import SessionAuthentication, BasicAuthentication from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response from rest_framework.views import APIView from rest_framework import status # GenericAPIView 进一步封装,把调用的类封装出来 class BookAPIView(APIView): ''' 查询所有图书,增加图书 ''' authentication_classes = (SessionAuthentication, BasicAuthentication) permission_classes = (IsAuthenticated,) def get(self, request, format=None): """ 查询所有图书 路由:GET /books/info?page=1&page_size=5 """ book_list = BookInfo.objects.all() # 2:创建可以序列化列表的序列化器 serializer_list = BookInfoSerializer(instance=book_list, many=True) # 3:转换数据 # return JsonResponse(serializer_list.data, safe=False) return Response(serializer_list.data, status=status.HTTP_200_OK)
二、权限Permissions
1、 How permissions are determined(如何确定权限)
AllowAny(所有用户) AllowAny权限类将允许不受限制的访问,而不管该请求是否已通过身份验证或未经身份验证。 IsAuthenticated(注册用户) IsAuthenticated 权限类将拒绝任何未经身份验证的用户的权限,并允许其他权限。 如果你希望你的API仅供注册用户访问,则此权限适用。 如果你希望你的API允许匿名用户读取权限,并且只允许对已通过身份验证的用户进行写入权限,则此权限是适合的。 IsAdminUser(管理员用户) 除非user.is_staff为True,否则IsAdminUser权限类将拒绝任何用户的权限,在这种情况下将允许权限。 如果你希望你的API只能被部分受信任的管理员访问,则此权限是适合的。 IsAuthenticatedOrReadOnly IsAuthenticatedOrReadOnly 将允许经过身份验证的用户执行任何请求。只有当请求方法是“安全”方法(GET, HEAD 或 OPTIONS)之一时,才允许未经授权的用户请求。 如果你希望你的API允许匿名用户读取权限,并且只允许对已通过身份验证的用户进行写入权限,则此权限是适合的。
1:Setting the permission policy 设置权限方案(setting文件进行全局)
'DEFAULT_PERMISSION_CLASSES': ( 'rest_framework.permissions.IsAuthenticated', )
2:Setting the permission policy 设置权限方案(局部全局:views.py)
permission_classes = (IsAuthenticated,) #局部权限
3:测试,管理员登录
from APP01.models import BookInfo, HeroInfo from APP01.serializer import BookInfoSerializer, HeroInfoSerializer from rest_framework.viewsets import ModelViewSet from rest_framework.authentication import SessionAuthentication, BasicAuthentication from rest_framework.permissions import IsAuthenticated, AllowAny,IsAdminUser # GenericAPIView 进一步封装,把调用的类封装出来 class BookAPIView(ModelViewSet): ''' 查询所有图书,增加图书 ''' # 1:局部认证 authentication_classes = [SessionAuthentication] # 2:局部权限 permission_classes = [IsAdminUser] queryset = BookInfo.objects.all() serializer_class = BookInfoSerializer
2:创建管理员
python manage.py createsuperuser
用户名:admin 密码:1
3:通过django中admin用户登录,
4:查看
三、限流Throttling
Twitter API rate limiting response, 一言以蔽之:限制响应次数1、 How throttling is determined(如何确定限流)
As with permissions and authentication, throttling in REST framework is always defined as a list of classes.
Before running the main body of the view each throttle in the list is checked. If any throttle check fails an exceptions.
Throttled exception will be raised, and the main body of the view will not run.
常见的限流类型
AnonRateThrottle The AnonRateThrottle will only ever throttle unauthenticated users. The IP address of the incoming request is used to generate a unique key to throttle against. The allowed request rate is determined from one of the following (in order of preference). The rate property on the class, which may be provided by overriding AnonRateThrottle and setting the property. The DEFAULT_THROTTLE_RATES['anon'] setting. AnonRateThrottle is suitable if you want to restrict the rate of requests from unknown sources.
限制匿名用户
UserRateThrottle The UserRateThrottle will throttle users to a given rate of requests across the API. The user id is used to generate a unique key to throttle against. Unauthenticated requests will fall back to using the IP address of the incoming request to generate a unique key to throttle against. The allowed request rate is determined from one of the following (in order of preference). The rate property on the class, which may be provided by overriding UserRateThrottle and setting the property. The DEFAULT_THROTTLE_RATES['user'] setting.
限流方案
1:Setting the throttle policy 设置限流方案(setting文件进行全局)
'DEFAULT_THROTTLE_CLASSES': [ 'rest_framework.throttling.AnonRateThrottle', 'rest_framework.throttling.UserRateThrottle' ], 'DEFAULT_THROTTLE_RATES': { 'anon': '100/day', 'user': '1000/day' }
2:Setting the throttle policy 设置局部限流方案(views文件进行)
from APP01.models import BookInfo, HeroInfo from APP01.serializer import BookInfoSerializer, HeroInfoSerializer from rest_framework.viewsets import ModelViewSet from rest_framework.authentication import SessionAuthentication, BasicAuthentication from rest_framework.permissions import IsAuthenticated, AllowAny, IsAdminUser from rest_framework.throttling import AnonRateThrottle,UserRateThrottle # GenericAPIView 进一步封装,把调用的类封装出来 class BookAPIView(ModelViewSet): ''' 查询所有图书,增加图书 ''' # 1:局部认证 authentication_classes = [SessionAuthentication] # 2:局部权限 permission_classes = [IsAdminUser] # 3:局部限流 throttle_classes = [UserRateThrottle] queryset = BookInfo.objects.all() serializer_class = BookInfoSerializer
3:测试
四、分页Pagination
Django provides a few classes that help you manage paginated data – that is, data that’s split across several pages, with “Previous/Next” links.
Django提供了上一页/下一页的方式来进行分页
1、 How pagination is determined(如何确定分页)
1:Setting the pagination policy 设置分页方案(setting文件进行全局)
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
'PAGE_SIZE': 100
}
2:Setting the pagination policy 设置局部限流方案(views文件进行)
from APP01.models import BookInfo, HeroInfo from APP01.serializer import BookInfoSerializer, HeroInfoSerializer from rest_framework.viewsets import ModelViewSet from rest_framework.authentication import SessionAuthentication, BasicAuthentication from rest_framework.permissions import IsAuthenticated, AllowAny, IsAdminUser from rest_framework.throttling import AnonRateThrottle, UserRateThrottle from rest_framework.pagination import LimitOffsetPagination,PageNumberPagination # GenericAPIView 进一步封装,把调用的类封装出来 class BookAPIView(ModelViewSet): ''' 查询所有图书,增加图书 ''' queryset = BookInfo.objects.all() serializer_class = BookInfoSerializer # 1:局部分页 pagination_class = PageNumberPagination
2、 自定义分页
PageNumberPagination中存在一个问题,即:page_size 无法修改,我们可以通过自定义类来实现
from APP01.models import BookInfo, HeroInfo from APP01.serializer import BookInfoSerializer, HeroInfoSerializer from rest_framework.viewsets import ModelViewSet from rest_framework.authentication import SessionAuthentication, BasicAuthentication from rest_framework.permissions import IsAuthenticated, AllowAny, IsAdminUser from rest_framework.throttling import AnonRateThrottle, UserRateThrottle from rest_framework.pagination import LimitOffsetPagination,PageNumberPagination # GenericAPIView 进一步封装,把调用的类封装出来 # 自定义分页对象 class MyPageNumberPagination(PageNumberPagination): page_size_query_param = "page_size" max_page_size = 5 #最大不能超过5 class BookAPIView(ModelViewSet): ''' 查询所有图书,增加图书 ''' queryset = BookInfo.objects.all() serializer_class = BookInfoSerializer # 1:自定义分页 pagination_class = MyPageNumberPagination
3、 自定义分页-封装返回数据
有时候,我们返回数据的时候需要返回一个状态码,而且不希望返回next和previous,这时我们可以重新封装分页组件,其中还可以通过JsonResponse去指定返回json格式数据。
from django.http import JsonResponse from rest_framework.pagination import PageNumberPagination from collections import OrderedDict class StandardBasePagination(PageNumberPagination): page_size = 10 page_size_query_param = 'page_size' max_page_size = 1000 def get_paginated_response(self, data): return JsonResponse(OrderedDict([ ('code', 20000), ('count', self.page.paginator.count), ('results', data) ]))
五、过滤
The root QuerySet provided by the Manager describes all objects in the database table. Usually, though, you'll need to select only a subset of the complete set of objects. 从数据库表中选择一些子集。
一、步骤
1:下载 pip install django-filter
2:配置setting
INSTALLED_APPS = [ ...... 'django_filters', ] REST_FRAMEWORK = { ...... 'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend'] }
2:视图设置
pip install django-filter You should now either add the filter backend to your settings: REST_FRAMEWORK = { 'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend'] } Or add the filter backend to an individual View or ViewSet. from django_filters.rest_framework import DjangoFilterBackend class UserListView(generics.ListAPIView): ... filter_backends = [DjangoFilterBackend] If all you need is simple equality-based filtering, you can set a filterset_fields attribute on the view, or viewset, listing the set of fields you wish to filter against. class ProductList(generics.ListAPIView): queryset = Product.objects.all() serializer_class = ProductSerializer filter_backends = [DjangoFilterBackend] filterset_fields = ['category', 'in_stock'] This will automatically create a FilterSet class for the given fields, and will allow you to make requests such as: http://example.com/api/products?category=clothing&in_stock=True
from APP01.models import BookInfo, HeroInfo from APP01.serializer import BookInfoSerializer, HeroInfoSerializer from rest_framework.viewsets import ModelViewSet from rest_framework.pagination import LimitOffsetPagination, PageNumberPagination from django_filters.rest_framework import DjangoFilterBackend # GenericAPIView 进一步封装,把调用的类封装出来 # 自定义分页对象 class MyPageNumberPagination(PageNumberPagination): page_size_query_param = "page_size" max_page_size = 5 # 最大不能超过5 class BookAPIView(ModelViewSet): ''' 查询所有图书,增加图书 ''' queryset = BookInfo.objects.all() serializer_class = BookInfoSerializer # 1:自定义分页 pagination_class = MyPageNumberPagination # 2:局部过滤 filter_backends = [DjangoFilterBackend] filterset_fields = ['id','btitle']
测试效果
六、排序OrderingFilter
from APP01.models import BookInfo, HeroInfo from APP01.serializer import BookInfoSerializer, HeroInfoSerializer from rest_framework.viewsets import ModelViewSet from rest_framework.pagination import LimitOffsetPagination, PageNumberPagination from rest_framework.filters import OrderingFilter from django_filters.rest_framework import DjangoFilterBackend # GenericAPIView 进一步封装,把调用的类封装出来 # 自定义分页对象 class MyPageNumberPagination(PageNumberPagination): page_size = 5 page_size_query_param = "page_size" max_page_size = 5 # 最大不能超过5 class BookAPIView(ModelViewSet): ''' 查询所有图书,增加图书 ''' queryset = BookInfo.objects.all() serializer_class = BookInfoSerializer # 1:自定义分页 pagination_class = MyPageNumberPagination # 2:局部过滤 filter_backends = [DjangoFilterBackend] filterset_fields = ['id','btitle'] # 3:排序 filter_backends = [OrderingFilter] ordering_fields = ['id', 'btitle','bread']
注意:
1:排序所使用的包:from rest_framework.filters import OrderingFilter
2:正序:?ordering=username
3:倒序:?
ordering=-username
4:多字段排序:?ordering=account,username
七、异常处理Exceptions
Exceptions… allow error handling to be organized cleanly in a central or high-level place within the program structure. — Doug Hellmann,
一、常见的异常类型
APIException:接口异常
Signature: APIException()
The base class for all exceptions raised inside an APIView class or @api_view.
AuthenticationFailed:验证失败
Signature: AuthenticationFailed(detail=None, code=None)
Raised when an incoming request includes incorrect authentication.
By default this exception results in a response with the HTTP status code "401 Unauthenticated", but it may also result in a "403 Forbidden" response, depending on the authentication scheme in use. See the authentication documentation for more details.
NotAuthenticated:未认证
Signature: NotAuthenticated(detail=None, code=None)
Raised when an unauthenticated request fails the permission checks.
By default this exception results in a response with the HTTP status code "401 Unauthenticated", but it may also result in a "403 Forbidden" response, depending on the authentication scheme in use. See the authentication documentation for more details.
PermissionDenied:没有权限
Signature: PermissionDenied(detail=None, code=None)
Raised when an authenticated request fails the permission checks.
By default this exception results in a response with the HTTP status code "403 Forbidden".
NotFound:未找到
Signature: NotFound(detail=None, code=None)
Raised when a resource does not exists at the given URL. This exception is equivalent to the standard Http404 Django exception.
By default this exception results in a response with the HTTP status code "404 Not Found".
MethodNotAllowed:方法未允许
Signature: MethodNotAllowed(method, detail=None, code=None)
Raised when an incoming request occurs that does not map to a handler method on the view.
By default this exception results in a response with the HTTP status code "405 Method Not Allowed".
NotAcceptable:不能接受的
Signature: NotAcceptable(detail=None, code=None)
Raised when an incoming request occurs with an Accept header that cannot be satisfied by any of the available renderers.
By default this exception results in a response with the HTTP status code "406 Not Acceptable".
UnsupportedMediaType:不支持的媒体类型
Signature: UnsupportedMediaType(media_type, detail=None, code=None)
Raised if there are no parsers that can handle the content type of the request data when accessing request.data.
By default this exception results in a response with the HTTP status code "415 Unsupported Media Type".
Throttled:节流
Signature: Throttled(wait=None, detail=None, code=None)
Raised when an incoming request fails the throttling checks.
By default this exception results in a response with the HTTP status code "429 Too Many Requests".
ValidationError:验证错误
Signature: ValidationError(detail, code=None)
The ValidationError exception is slightly different from the other APIException classes:
二、自定义异常
1:Setting the permission policy 设置权限方案(setting文件进行全局)
'DEFAULT_PERMISSION_CLASSES': (
'EXCEPTION_HANDLER': 'APP01.my_exception.custom_exception_handler'
)
2:创建一个自定义异常类
'''自定义异常处理类''' from rest_framework.views import exception_handler from rest_framework.response import Response def custom_exception_handler(exc, context): # Call REST framework's default exception handler first, # to get the standard error response. response = exception_handler(exc, context) # Now add the HTTP status code to the response. if response is not None: response.data['status_code'] = response.status_code return Response("漂亮的错误页面")