Django Rest_Framework (3)


In order to facilitate the following learning, we create a new sub-application opt

python manage.py startapp opt

Register sub-application

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',
    'students',
    'sers',    # 序列化器
    "school",  # 序列化器嵌套
    'req',     # 请求与响应
    'demo',    # 视图
    'opt',     # drf提供的组件使用
]

Overall routing, code:

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('students/', include("students.urls")),
    path('sers/', include("sers.urls")),
    path('school/', include("school.urls")),
    path("req/", include("req.urls")),
    path("demo/", include("demo.urls")),
    path("opt/", include("opt.urls")),
]

Subrouting, code:

from django.urls import path
from . import views
urlpatterns = [

]

Because the login function needs to be used in the next authentication component, we use django's built-in admin site and create an administrator.

The access address of the admin operation site: http://127.0.0.1:8000/admin

python manage.py createsuperuser
# 如果之前有账号,但是忘了,可以通过终端下的命令修改指定用户的密码,这里的密码必须8位长度以上的
python manage.py changepassword 用户名

[The external link image transfer failed. The source site may have an anti-leeching mechanism. It is recommended to save the image and upload it directly (img-5SSBmyPI-1691197559274)(assets/1557276390641.png)]

After creating an administrator, visit the admin site and modify the language configuration of the site first

settings.py

LANGUAGE_CODE = 'zh-hans'

TIME_ZONE = 'Asia/Shanghai'

[The external link image transfer failed. The source site may have an anti-leeching mechanism. It is recommended to save the image and upload it directly (img-H1xGvmiC-1691197559275)(assets/1553043081445.png)]

The effect of visiting the admin site:

[The external link image transfer failed. The source site may have an anti-leeching mechanism. It is recommended to save the image and upload it directly (img-DCDiDFhj-1691197559275)(assets/1553043054133.png)]

1. Authentication

The global default authentication scheme can be configured in the configuration file

Common authentication methods: cookie, session, token

/site-packages/rest_framework/settings.py default configuration file

REST_FRAMEWORK = {
    
    
    # 配置认证方式的选项
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.SessionAuthentication', # session认证
        'rest_framework.authentication.BasicAuthentication',   # basic认证
    )
}

You can also set separate different authentication methods by setting the authentication_classess class attribute in a specific view class

from rest_framework.authentication import SessionAuthentication, BasicAuthentication
from rest_framework.views import APIView

class ExampleView(APIView):
    # 类属性
    authentication_classes = [SessionAuthentication, BasicAuthentication]
    def get(self,request):
        pass

There are two possible return values ​​when authentication fails, which we need to use with the permission component:

  • 401 Unauthorized Unauthenticated
  • 403 Permission Denied permission is forbidden

Custom authentication, drfdemo.authenticationcode:

from rest_framework.authentication import BaseAuthentication
from django.contrib.auth import get_user_model

class CustomAuthentication(BaseAuthentication):
    """
    自定义认证方式
    """
    def authenticate(self, request):
        """
        认证方法
        request: 本次客户端发送过来的http请求对象
        """
        user = request.query_params.get("user")
        pwd  = request.query_params.get("pwd")
        if user != "root" or pwd != "houmen":
            return None
        # get_user_model获取当前系统中用户表对应的用户模型类
        user = get_user_model().objects.first()
        return (user, None)  # 按照固定的返回格式填写 (用户模型对象, None)

View calls custom authentication, view code:

from django.contrib.auth.models import AnonymousUser
from django.shortcuts import render
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.authentication import SessionAuthentication
from drfdemo.authentication import CustomAuthentication
# Create your views here.
class HomeAPIView(APIView):
    # authentication_classes = [CustomAuthentication, ]
    def get(self,request):
        """单独设置认证方式"""
        print(request.user) # 在中间件AuthenticationMiddleware中完成用户身份识别的,如果没有登录request.user值为AnonymousUser
        if request.user.id is None:
            return Response("未登录用户:游客")
        else:
            return Response(f"已登录用户:{
      
      request.user}")

Of course, you can also comment out the configuration in the above view and change it to global configuration. settings.py, code:

"""drf配置信息必须全部写在REST_FRAMEWORK配置项中"""
REST_FRAMEWORK = {
    
    
    # 配置认证方式的选项【drf的认证是内部循环遍历每一个注册的认证类,一旦认证通过识别到用户身份,则不会继续循环】
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'drfdemo.authentication.CustomAuthentication',          # 自定义认证
        'rest_framework.authentication.SessionAuthentication',  # session认证
        'rest_framework.authentication.BasicAuthentication',    # 基本认证
    )
}

2. Permissions

Permission control can restrict user access to views and access to model objects.

  • Before executing the dispatch() method of the view's as_view() method, the view access permissions will be determined first.
  • When obtaining a specific model object through get_object(), the access rights of the model object will be judged.

use

You can set the default permission management class globally in the configuration file settings , such as

REST_FRAMEWORK = {
    
    
    ....
    
    'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.IsAuthenticated',
    )
}

If not specified, the following default configuration rest_framework/settings.py is used

'DEFAULT_PERMISSION_CLASSES': (
   'rest_framework.permissions.AllowAny',
)

You can also make local settings through the permission_classes attribute in a specific view , such as

from django.contrib.auth.models import AnonymousUser
from django.shortcuts import render
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.authentication import SessionAuthentication
from drfdemo.authentication import CustomAuthentication
# Create your views here.
from rest_framework.permissions import AllowAny,IsAuthenticated
class HomeAPIView(APIView):
    # authentication_classes = [CustomAuthentication, ]
    # permission_classes = [AllowAny]       # 如果项目中大部分视图接口不许直接访问,少数接口可以直接访问,则可以全局配置权限认证,仅在部分不需要认证的视图类中加上AllowAny即可
    permission_classes = [IsAuthenticated]  # 如果项目中大部分视图接口允许直接访问,少数接口必修认证权限,则可以不要权限认证的全局配置,仅在需要认证权限的视图下,局部配置IsAuthenticated即可
    def get(self,request):
        """单独设置认证方式"""
        print(request.user) # 在中间件AuthenticationMiddleware中完成用户身份识别的,如果没有登录request.user值为AnonymousUser
        if request.user.id is None:
            return Response("未登录用户:游客")
        else:
            return Response(f"已登录用户:{
      
      request.user}")

Permissions provided

  • AllowAny allows all users, default permissions
  • IsAuthenticated Only users authenticated by login
  • IsAdminUser Admin users only
  • IsAuthenticatedOrReadOnly Users who have logged in and authenticated can add, delete, and modify data. Users who have not logged in and authenticated can only view the data.

Example

View code:

from rest_framework.viewsets import ModelViewSet
from school.models import Student
from school.serializers import StudentModelSerializer
from rest_framework.permissions import IsAuthenticatedOrReadOnly
class Hom2APIView(ModelViewSet):
    authentication_classes = [CustomAuthentication, ]
    # 游客仅能查看数据,登录用户可以进行数据的修改/添加/删除
    permission_classes = [IsAuthenticatedOrReadOnly]
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer

Routing code:

from django.urls import path
from . import views
urlpatterns = [
    path("s1/", views.HomeAPIView.as_view()),
]

from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register("s2", views.Hom2APIView, "s2")
urlpatterns += router.urls

Custom permissions

If you need to customize permissions, you need to inherit the rest_framework.permissions.BasePermission parent class and implement any one or both of the following two methods

  • .has_permission(self, request, view)

    Whether the view can be accessed, view represents the current view object

  • .has_object_permission(self, request, view, obj)

    Whether the model object can be accessed, view represents the current view, and obj is the model data object

For example:

Under the current sub-application, create a permission file drfdemo.permissions.py and declare a custom permission class:

from rest_framework.permissions import BasePermission
class IsXiaoMingPermission(BasePermission):
    """
    自定义权限,可用于全局配置,也可以用于局部配置
    """

    def has_permission(self, request, view):
        """
        视图权限
        返回结果未True则表示允许访问视图类
        request: 本次客户端提交的请求对象
        view: 本次客户端访问的视图类
        """
        # # 写在自己要实现认证的代码过程。
        # user = request.query_params.get("user")
        # # 返回值为True,则表示通行
        # return user == "xiaoming"
        return True

    def has_object_permission(self, request, view, obj):
        """
        模型权限,写了视图权限(has_permission)方法,一般就不需要写这个了。
        返回结果未True则表示允许操作模型对象
        """
        from school.models import Student
        if isinstance(obj, Student):
            # 限制只有小明才能操作Student模型
            user = request.query_params.get("user")
            return user == "xiaoming"  # 如果不是xiaoming,返回值为False,不能操作
        else:
            # 操作其他模型,直接放行
            return True

View code:

from rest_framework.viewsets import ModelViewSet
from school.models import Student
from school.serializers import StudentModelSerializer
from rest_framework.permissions import IsAuthenticatedOrReadOnly
from drfdemo.permissions import IsXiaoMingPermission
class Hom2APIView(ModelViewSet):
    # authentication_classes = [CustomAuthentication, ]
    # 游客仅能查看数据,登录用户可以进行数据的修改/添加/删除
    # permission_classes = [IsAuthenticatedOrReadOnly]

    # 自定义权限
    permission_classes = [IsXiaoMingPermission]
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer

Example code for authentication and permissions:

settings.py, global configuration, code:

"""drf配置信息必须全部写在REST_FRAMEWORK配置项中"""
REST_FRAMEWORK = {
    
    
    # 配置认证方式的选项【drf的认证是内部循环遍历每一个注册的认证类,一旦认证通过识别到用户身份,则不会继续循环】
    'DEFAULT_AUTHENTICATION_CLASSES': (
        # 'drfdemo.authentication.CustomAuthentication',          # 自定义认证
        'rest_framework.authentication.SessionAuthentication',  # session认证
        'rest_framework.authentication.BasicAuthentication',    # 基本认证
    ),
    # 权限设置[全局配置,在视图中可以通过permission_classes进行局部配置,局部配置优先级高于全局配置]
    'DEFAULT_PERMISSION_CLASSES': (
        # 'rest_framework.permissions.IsAuthenticated',
        'drfdemo.permissions.IsXiaoMingPermission',
    ),
}

The main function of authentication is to identify the identity of the client's visitor, but it cannot intercept the client's access.

Permissions are implemented based on authentication, but permissions can be used to intercept users' access to views and models for users with different identities.

3. Throttling

The frequency of interface access can be limited to reduce server pressure or to implement specific services.

Generally used for paid purchases, voting and other scenarios.

basic use

DEFAULT_THROTTLE_CLASSESYou can use and DEFAULT_THROTTLE_RATESfor global configuration in the configuration file ,

REST_FRAMEWORK = {
    
    
    # 限流全局配置
    # 'DEFAULT_THROTTLE_CLASSES':[ # 限流配置类
    #     'rest_framework.throttling.AnonRateThrottle', # 未认证用户[未登录用户]
    #     'rest_framework.throttling.UserRateThrottle', # 已认证用户[已登录用户]
    # ],
    'DEFAULT_THROTTLE_RATES':{
    
     # 频率配置
        'anon': '2/day',  # 针对游客的访问频率进行限制,实际上,drf只是识别首字母,但是为了提高代码的维护性,建议写完整单词
        'user': '5/day', # 针对会员的访问频率进行限制,
    }
}

DEFAULT_THROTTLE_RATESYou can use second, minute, houror dayto specify the period.

It can also be configured through the throttle_classess attribute in a specific view, such as

from rest_framework.throttling import UserRateThrottle
class Student2ModelViewSet(ModelViewSet):
    queryset = Student.objects
    serializer_class = StudentModelSerializer
    # 限流局部配置[这里需要配合在全局配置中的DEFAULT_THROTTLE_RATES来设置频率]
    throttle_classes = [UserRateThrottle]

Optional current limiting class

1) AnonRateThrottle

Restrict all anonymous unauthenticated users and use IP to distinguish users. [Many companies use IP combined with device information to judge, which is of course a little more reliable than IP]

Use DEFAULT_THROTTLE_RATES['anon']to set frequency

2)UserRateThrottle

To limit authenticated users, use the id primary key of the User model to distinguish them.

Use DEFAULT_THROTTLE_RATES['user']to set frequency

3)ScopedRateThrottle

Limit the user's access frequency to each view, using IP or user ID.

settings.py, code:

"""drf配置信息必须全部写在REST_FRAMEWORK配置项中"""
REST_FRAMEWORK = {
    
    
    # 配置认证方式的选项【drf的认证是内部循环遍历每一个注册的认证类,一旦认证通过识别到用户身份,则不会继续循环】
    'DEFAULT_AUTHENTICATION_CLASSES': (
        # 'drfdemo.authentication.CustomAuthentication',          # 自定义认证
        'rest_framework.authentication.SessionAuthentication',  # session认证
        'rest_framework.authentication.BasicAuthentication',    # 基本认证
    ),
    # 权限设置[全局配置,在视图中可以通过permission_classes进行局部配置,局部配置优先级高于全局配置]
    'DEFAULT_PERMISSION_CLASSES': (
        # 'rest_framework.permissions.IsAuthenticated',
        'drfdemo.permissions.IsXiaoMingPermission',
    ),
    # 限流全局配置
    'DEFAULT_THROTTLE_CLASSES':[  # 限流配置类
    #     'rest_framework.throttling.AnonRateThrottle',  # 未认证用户[未登录用户]
    #     'rest_framework.throttling.UserRateThrottle',  # 已认证用户[已登录用户]
        'rest_framework.throttling.ScopedRateThrottle',  # 基于自定义的命名空间来限流
    ],
    'DEFAULT_THROTTLE_RATES': {
    
      # 频率配置
        'anon': '1/s',  # 针对游客的访问频率进行限制,实际上,drf只是识别首字母,但是为了提高代码的维护性,建议写完整单词
        'user': '1/s',  # 针对会员的访问频率进行限制,
        'Hom3': '3/m',  # 针对自定义命名空间,进行限流
    }
}

View code:

from rest_framework.throttling import AnonRateThrottle,UserRateThrottle
class Hom3APIView(APIView):
    # authentication_classes = [CustomAuthentication]  # 调用自定义认证
    # 局部配置限流
    # throttle_classes = [AnonRateThrottle, UserRateThrottle]
    # 配置自定义限流
    throttle_scope = "Hom3"   # 自定义命名空间[少用,因为对于大部分的集体环境下都是公用一个IP地址]
    def get(self,request):
        return Response(f"访问了视图")

4. Filtering

For list data that may need to be filtered based on fields, we can enhance support by adding the django-fitlter extension.

pip install django-filter

settings.py, code:

INSTALLED_APPS = [
    # ....
    'django_filters',
]

Add the global settings of the filter class in the configuration file:

"""drf配置信息必须全部写在REST_FRAMEWORK配置项中"""
REST_FRAMEWORK = {
    
    
    # ....代码省略。。。
    
    # 过滤查询,全局配置
    'DEFAULT_FILTER_BACKENDS': [
        'django_filters.rest_framework.DjangoFilterBackend'
    ],
}

Add the class attribute filter_fields to the view class to specify the fields that can be filtered

class Hom4APIView(ModelViewSet):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer
    filter_fields = ["sex","classmate"]
    # list方法中进行调用->调用了GenericAPIView中声明的filter_queryset方法---> 配置中的过滤器类的filter_queryset---> filter_fields
    
# 单个字段过滤
# http://127.0.0.1:8000/opt/s4/?classmate=301
# http://127.0.0.1:8000/opt/s4/?sex=1
# 多个字段过滤
# http://127.0.0.1:8000/opt/s4/?classmate=502&sex=2

Routing code:

from django.urls import path,include
from . import views

from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register("s2", views.Hom2APIView, "s2")
router.register("s4", views.Hom4APIView, "s4")

urlpatterns = [
    path("s1/", views.HomeAPIView.as_view()),
    path("", include(router.urls)),  # 等价于  urlpatterns += router.urls
    path("s3/", views.Hom3APIView.as_view()),
    path("s3/", views.Hom3APIView.as_view()),
]

Local settings, directly in the view, specify the filter class called in the current view class

# 先注释掉settings中的全局过滤配置
from rest_framework.viewsets import ModelViewSet
from students.models import Student
from students.serializers import StudentModelSerializer
# Create your views here.
from django_filters.rest_framework import DjangoFilterBackend
class Hom4APIView(ModelViewSet):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer
    filter_backends = [DjangoFilterBackend, ]
    filter_fields = ["sex","classmate"]
    # list方法中进行调用->调用了GenericAPIView中声明的filter_queryset方法---> 配置中的过滤器类的filter_queryset---> filter_fields

5. SortingOrdering

For list data, REST framework provides the OrderingFilter filter to help us quickly specify that the data is sorted according to the specified field.

Instructions:

Set filter_backends in the class view and use rest_framework.filters.OrderingFilterthe filter. The REST framework will check whether the ordering parameter is included in the query string parameter of the request. If the ordering parameter is included, the data set will be sorted according to the sorting field specified by the ordering parameter.

The optional field value of the ordering parameter that can be passed by the front end needs to be specified in ordering_fields.

Configuration file, settings.py, code:

"""drf配置信息必须全部写在REST_FRAMEWORK配置项中"""
REST_FRAMEWORK = {
    
    
    # 。。。代码省略
    # 过滤查询,全局配置
    # 过滤和排序使用了一个公用的配置项,所以2个组件要么一起全局配置,要么就一起局部配置
    'DEFAULT_FILTER_BACKENDS': [
    #     'django_filters.rest_framework.DjangoFilterBackend',  # 过滤
        'rest_framework.filters.OrderingFilter',  # 排序
    ],
}

View code:

from rest_framework.viewsets import ModelViewSet
from students.models import Student
from students.serializers import StudentModelSerializer
# Create your views here.
from django_filters.rest_framework import DjangoFilterBackend
class Hom4APIView(ModelViewSet):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer
    # 局部过滤
    # filter_backends = [DjangoFilterBackend, OrderingFilter]
    # 过滤字段
    # filter_fields = ["sex","classmate"]
    # 数据排序
    ordering_fields = ['id', 'age']

# 127.0.0.1:8000/books/?ordering=-age
# -id 表示针对id字段进行倒序排序
# id  表示针对id字段进行升序排序

As mentioned above, because filtering and sorting share a common configuration item, if sorting and filtering are to be used together, the entire project must be filtered and sorted together globally, or filtered and sorted locally. There must never be a situation where there is a global configuration and a local configuration. The local configuration will overwrite the global configuration.

from rest_framework.viewsets import ModelViewSet
from students.models import Student
from students.serializers import StudentModelSerializer
# Create your views here.
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.filters import OrderingFilter
class Hom4APIView(ModelViewSet):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer
    # 局部过滤
    filter_backends = [DjangoFilterBackend, OrderingFilter]
    # 过滤字段
    filter_fields = ["sex","classmate"]
    # list方法中进行调用->调用了GenericAPIView中声明的filter_queryset方法---> 配置中的过滤器类的filter_queryset---> filter_fields

    # 数据排序
    ordering_fields = ['id', 'age']
    
# http://127.0.0.1:8000/opt/demo4/?ordering=id&classmate=301

6. Pagination

Because the paginator provided by django by default is mainly used in business scenarios where the front and back ends are not separated, the REST framework also provides support for paging.

We can set the global paging method in the configuration file, such as:

REST_FRAMEWORK = {
    
    
    # 分页,全局配置
    # 页码分页器,  ?page=页码&page_size=单页数据量
    # 'DEFAULT_PAGINATION_CLASS':  'rest_framework.pagination.PageNumberPagination',
    # 偏移量分页器, ?limit=单页数据量&offset=数据开始下标
    'DEFAULT_PAGINATION_CLASS':  'rest_framework.pagination.LimitOffsetPagination',
    'PAGE_SIZE': 10  # 每页数目,如果不设置,则没有进行分配
}
# 如果在settings.py配置文件中, 设置了全局分页,那么在drf中凡是调用了ListModelMixin的list(),都会自动分页。如果项目中出现大量需要分页的数据,只有少数部分的不需要分页,则可以在少部分的视图类中关闭分页功能。
# 另外,视图类在使用过分页以后,务必在编写queryset属性时,模型.objects后面调用结果。例如:
# Student.objects.all()
class Hom5APIView(ListAPIView):
	pagination_class = None

You can also add different paging behaviors to the view by customizing the Pagination class. pagination_clasSpecified through properties in the view .

Optional pager

1) PageNumberPagination

Front-end access URL form:

GET  http://127.0.0.1:8000/students/?page=4

Properties that can be defined in subclasses:

  • page_size Number of pages per page
  • page_query_param The page number keyword name sent by the front end, the default is "page"
  • page_size_query_param Keyword name for each page sent by the front end, default is None
  • max_page_size The maximum number of pages that can be set by the front end

Paginator class, paginations, code:

from  rest_framework.pagination import PageNumberPagination,LimitOffsetPagination
# PageNumberPagination,以页码作为分页条件
# page=1&page_size=10      第1页
# page=2&page_size=10      第2页
# ...
# LimitOffsetPagination,以数据库查询的limit和offset数值作为分页条件
# limit=10&offset=0   第1页
# limit=10&offset=10  第2页
# ...

# 自定义分页器,PageNumberPagination
class StudentPageNumberPagination(PageNumberPagination):
    page_query_param = "page" # 查询字符串中代表页码的变量名
    page_size_query_param = "size" # 查询字符串中代表每一页数据的变量名
    page_size = 2 # 每一页的数据量
    max_page_size = 4 # 允许客户端通过查询字符串调整的最大单页数据量

View, views, code:


from rest_framework.pagination import LimitOffsetPagination, PageNumberPagination

# 分页类往往会单独的保存到一个独立的模块,例如:当前子应用目录下创建一个pagination.py保存,使用时导包
class Hom5PageNumberPagination(PageNumberPagination):
    page_size = 10                  # 默认分页的每一页数据量
    max_page_size = 20              # 设置允许客户端通过地址栏参数调整的最大单页数据量
    page_query_param = "pn"         # 地址栏上代表页码的变量名,默认是 page
    page_size_query_param = "size"  # 地址栏上代表单页数据量的变量名,默认是page_size

class Hom5APIView(ModelViewSet):
    # queryset = Student.objects  # 这句话在没有进行分页时不会报错,调用了分页则会报错!
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer

    # 局部分页
    # pagination_class = None  # 关闭分页功能
    # 局部分页往往采用自定义分页类,进行分页数据量的配置
    pagination_class = Hom5PageNumberPagination

    def list(self, request, *args, **kwargs):
        # 获取django的配置项
        from django.conf import settings

        # 获取rest_framework的配置项
        # from rest_framework.settings import api_settings
        # print(api_settings.DEFAULT_PAGINATION_CLASS)
        return super().list(request, *args, **kwargs)

2)LimitOffsetPagination

Front-end access URL form:

GET http://127.0.0.1/four/students/?limit=100&offset=100

Properties that can be defined in subclasses:

  • default_limit default limit, the default value PAGE_SIZEis consistent with the setting
  • limit_query_param limit parameter name, default 'limit'
  • offset_query_param offset parameter name, default 'offset'
  • max_limit maximum limit limit, default None

Pagination class, code:

from  rest_framework.pagination import PageNumberPagination,LimitOffsetPagination
# PageNumberPagination,以页码作为分页条件
# page=1&size=10      第1页
# page=2&size=10      第2页
# LimitOffsetPagination,以数据库查询的limit和offset数值作为分页条件
# limit=10&offset=0   第1页
# limit=10&offset=10  第2页

# LimitOffsetPagination
class StudentLimitOffsetPagination(LimitOffsetPagination):
    limit_query_param = "limit" # 查询字符串中代表每一页数据的变量名
    offset_query_param = "offset" # 查询字符串中代表页码的变量名
    default_limit = 2 # 每一页的数据量
    max_limit = 4 # 允许客户端通过查询字符串调整的最大单页数据量

view, views, code:

from .paginations import StudentPageNumberPagination,StudentLimitOffsetPagination
class Student3ModelViewSet(ModelViewSet):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer
    # 取消当前视图类的分页效果
    # pagination_class = None
    # 局部分页
    pagination_class = StudentLimitOffsetPagination

7. Exception handlingExceptions

The REST framework itself provides exception handling in APIView, but it only formats exceptions related to existing interface development within drf. However, during development we will also use various data or make various network requests, which are all possible resulting in exceptions. These exceptions are not handled in drf, so they will bubble up to the django framework. The django framework will organize the error information and return it to the client as an html page. It may be in the front-end and back-end separation project. js cannot understand or receive this kind of data, which may even cause js to cause errors. Therefore, in order to avoid this situation, we can customize an exception handling function of our own. For exceptions that drf cannot handle, we write the code logic for exception handling ourselves.

For the existing drf exception handling, additional logic code belonging to the developer is added. Generally, the exception handling function we write will be written in a public directory or the main application directory. Here, create an exceptions.py directly under the main application, code:

from rest_framework.views import exception_handler
from rest_framework.response import Response
from rest_framework import status
# 针对mysql、mongoDB、redis、第三方数据接口
def custom_exception_handler(exc, context):
    """
    自定义异常函数
    exc: 异常实例对象,发生异常时实例化出来的
    context: 字典,异常发生时python解释器收集的执行上下文信息。
             所谓的执行上下文就是python解释器在执行代码时保存在内存中的变量、函数、类、对象、模块等一系列的信息组成的环境信息。
    """
    response = exception_handler(exc, context)

    if response is None:
        """当前异常,drf无法处理"""
        if isinstance(exc, ZeroDivisionError):
            response = Response({
    
    "detail":"数学老师还有30秒达到战场,0不能作为除数!"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
    return response

Declare custom exception handling in the configuration file, settings, code:

REST_FRAMEWORK = {
    
    
    # 异常配置
    'EXCEPTION_HANDLER': 'drfdemo.exceptions.custom_exception_handler'
}

If no custom exception is declared, drf will adopt the default method and use its own encapsulated exception handling function, rest_frame/settings.py

REST_FRAMEWORK = {
    
    
    'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler'
}

For example:

In addition to handling exceptions about the database, here are other exceptions as examples:

主应用.exceptions, code:

from rest_framework.views import exception_handler
from rest_framework.response import Response
from rest_framework import status
from django.db import DatabaseError
# 针对mysql、mongoDB、redis、第三方数据接口
def custom_exception_handler(exc, context):
    """
    自定义异常函数
    exc: 异常实例对象,发生异常时实例化出来的
    context: 字典,异常发生时python解释器收集的执行上下文信息。
             所谓的执行上下文就是python解释器在执行代码时保存在内存中的变量、函数、类、对象、模块等一系列的信息组成的环境信息。
    """
    response = exception_handler(exc, context)
    print(f"context={
      
      context}")
    if response is None:
        """当前异常,drf无法处理"""
        view = context["view"] # 获取异常发生时的视图类
        request = context["request"] # 获取异常发生时的客户端请求对象
        if isinstance(exc, ZeroDivisionError):
            response = Response({
    
    "detail":"数学老师还有30秒达到战场,0不能作为除数!"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        if isinstance(exc, TypeError):
            print('[%s]: %s' % (view, exc))
            response = Response({
    
    'detail': '服务器内部错误'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

    return response

In the view, an error is reported intentionally:

from rest_framework.pagination import LimitOffsetPagination, PageNumberPagination

class Hom5PageNumberPagination(PageNumberPagination):
    page_size = 10                  # 默认分页的每一页数据量
    max_page_size = 20              # 设置允许客户端通过地址栏参数调整的最大单页数据量
    page_query_param = "pn"         # 地址栏上代表页码的变量名,默认是 page
    page_size_query_param = "size"  # 地址栏上代表单页数据量的变量名,默认是page_size

class Hom5APIView(ModelViewSet):
    queryset = Student.objects  # 这句话在没有进行分页时不会报错,调用了分页则会报错,所以会进入异常处理!
    # queryset = Student.objects.all()
    serializer_class = StudentModelSerializer

    # 局部分页
    # pagination_class = None  # 关闭分页功能
    # 局部分页往往采用自定义分页类,进行分页数据量的配置
    pagination_class = Hom5PageNumberPagination

    def list(self, request, *args, **kwargs):
        # 获取django的配置项
        from django.conf import settings

        # 获取rest_framework的配置项
        # from rest_framework.settings import api_settings
        # print(api_settings.DEFAULT_PAGINATION_CLASS)
        return super().list(request, *args, **kwargs)

Exceptions defined by REST framework

  • APIException The parent class of all exceptions
  • ParseError parsing error
  • AuthenticationFailed Authentication failed
  • NotAuthenticated Not authenticated yet
  • PermissionDenied Permission denied
  • NotFound 404 Not Found
  • MethodNotAllowed request method is not supported
  • NotAcceptable The data format to be obtained is not supported
  • Throttled The number of times the current limit has been exceeded
  • ValidationError verification failed

In other words, many exceptions not listed above require us to handle them ourselves in custom exceptions.

8. Automatically generate interface documents

REST framework can automatically help us generate interface documents.

Interface documents are presented in the form of web pages.

Automatic interface documentation can generate APIViewviews inherited from and its subclasses.

coreapi

Install dependencies

coreapiREST framewrok requires library support to generate interface documents .

pip install coreapi

Set interface document access path

Add the interface document path in the total route.

The view configuration corresponding to document routing is rest_framework.documentation.include_docs_urls,

The parameter titleis the title of the interface document website. Total routing urls.py, code:

from rest_framework.documentation import include_docs_urls

urlpatterns = [
    ...
    path('docs/', include_docs_urls(title='站点页面标题'))
]

Configure the interface document in settings.py.

INSTALLED_APPS = [
 
    'coreapi',


]
REST_FRAMEWORK = {
    
    
    # 。。。 其他选项
    # 接口文档
    'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.AutoSchema',
}

8.3. Where document descriptions are defined

1) For a single method view, you can directly use the documentation string of the class view, such as

class BookListView(generics.ListAPIView):
    """
    返回所有图书信息
    """

2) For views that contain multiple methods, separate method definitions in the documentation string of the class view, such as

class BookListCreateView(generics.ListCreateAPIView):
    """
    get:
    返回所有图书信息.

    post:
    新建图书.
    """

3) For the view set ViewSet, the definition is still enclosed in the documentation string of the class view, but it should be distinguished by the action name, such as

class BookInfoViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, GenericViewSet):
    """
    list:
    返回图书列表数据

    retrieve:
    返回图书详情数据

    latest:
    返回最新的图书数据

    read:
    修改图书的阅读量
    """

Visit the interface documentation web page

Visit 127.0.0.1:8000/docs/ with your browser to see the automatically generated interface document.

[The external link image transfer failed. The source site may have an anti-leeching mechanism. It is recommended to save the image and upload it directly (img-wJtyTHNM-1691197559276) (assets/interface document page.png)]

Two notes:

1) The retrieve name in the view set ViewSet is called read in the interface documentation website

2) The description of the parameter needs to be defined with the help_text option in the field of the model class or serializer class, such as:

class Student(models.Model):
    ...
    age = models.IntegerField(default=0, verbose_name='年龄', help_text='年龄')
    ...

or

class StudentSerializer(serializers.ModelSerializer):
    class Meta:
        model = Student
        fields = "__all__"
        extra_kwargs = {
    
    
            'age': {
    
    
                'required': True,
                'help_text': '年龄'
            }
        }

yasg

Install

pip install drf-yasg

Configuration, settings.py, code:

INSTALLED_APPS = [

    'drf_yasg',  # 接口文档drf_yasg

]

total route,

from django.contrib import admin
from django.urls import path, include
from rest_framework.documentation import include_docs_urls

# yasg的视图配置类,用于生成a'pi
from drf_yasg.views import get_schema_view
from drf_yasg import openapi
schema_view = get_schema_view(
    openapi.Info(
        title="drf接口文档",  # 必传
        default_version='v1.0,0',  # 必传
        description="描述信息",
        terms_of_service='',
        contact=openapi.Contact(email="[email protected]"),
        license=openapi.License(name="协议版本")
    ),
    public=True,
    # permission_classes=(rest_framework.permissions.AllowAny)  # 权限类
)


urlpatterns = [
    # 文档路由
    path('doc/', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger'),
    path('docs/', include_docs_urls(title='站点页面标题')),
    path('admin/', admin.site.urls),
    path('students/', include("students.urls")),
    path('sers/', include("sers.urls")),
    path('school/', include("school.urls")),
    path("req/", include("req.urls")),
    path("demo/", include("demo.urls")),
    path("opt/", include("opt.urls")),
]

http://127.0.0.1:8000/doc/, access effect:

[The external link image transfer failed. The source site may have an anti-leeching mechanism. It is recommended to save the image and upload it directly (img-6dSlTZQE-1691197559276) (assets/image-20210901182801990.png)]

[The external link image transfer failed. The source site may have an anti-leeching mechanism. It is recommended to save the image and upload it directly (img-RYVYIXsn-1691197559277) (assets/image-20210901182739563.png)]

Guess you like

Origin blog.csdn.net/weixin_53909748/article/details/132115590