斑马斑马-23-Django REST Framework (DRF)系列教程

学习和使用一个技术、官方文档至关重要。

一、认证Authentication

Auth needs to be pluggable.— Jacob Kaplan-Moss, 一言以蔽之:认证需要可插拔 
1、 How authentication is determined(如何确定身份验证)
常见的认证方式
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. 
使用HTTP基本认证,基于用于名和密码。多用于测试。如果用于生产环境,确保是https,而且要确保进行持久化存储
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.
该认证使用django默认的后端session验证。
如果成功通过验证,会提供(request.user、request.auth)
为了保证登录受保护,请始终使用Django标准视图登录
1:Setting the authentication scheme 设置认证方案(setting文件进行全局)
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.BasicAuthentication',
        'rest_framework.authentication.SessionAuthentication',
    ]}
2:Setting the authentication scheme 设置认证方案(类视图)
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)
views中进行视图设置

二、权限Permissions

Authentication or identification by itself is not usually sufficient to gain access to information or code. For that, the entity requesting access must have authorization.
身份验证或身份识别本身通常不足以获取信息或代码的访问权限。因此,请求访问的实体必须具有授权。

 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
View15.py

 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
view15.py

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
views.py

 

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
view16.py

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)
        ]))
pagination.py

 

五、过滤

 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']

}
settings.py

  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']
view17.py

 

 注意:

  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("漂亮的错误页面")
custom_exception_handler

  

 

 

posted @ 2020-07-26 14:09  逍遥小天狼  阅读(366)  评论(0编辑  收藏  举报