一 认证Authentication
开发中常见的认证方式:cookie、session、token。需要配合权限组件来使用。
创建一个新的子应用 component
?1 |
python manage.py startapp component
|
1 在配置文件setting.py中配置全局的认证方式/认证方案
?1 2 3 4 5 6 7 8 |
REST_FRAMEWORK = {
# 配置认证方式的选项【drf的认证是内部循环遍历每一个注册的认证类,一旦认证通过识别到用户身份,则不会继续循环】
'DEFAULT_AUTHENTICATION_CLASSES' : (
# 'component.authentication.CustomAuthentication', # 自定义认证
'rest_framework.authentication.SessionAuthentication' , # session认证
'rest_framework.authentication.BasicAuthentication' , # basic认证[基于账号密码]
)
}
|
提供的认证方式
- SessionAuthentication
- BasicAuthentication
- 自定义认证
2 自定义认证,component/authentication.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
from rest_framework.authentication import BaseAuthentication
from django.contrib.auth import get_user_model # 自动识别当前django系统中的系统用户模型
class CustomAuthentication(BaseAuthentication):
"""
自定义认证方式
"""
def authenticate( self , request):
"""核心认证方法"""
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. filter (is_superuser = 1 , is_active = 1 ).first()
return (user, None ) # 按照固定的返回格式填写 (用户模型对象, None)
|
3 可以在具体的视图类文件view.py中通过类属性authentication_classess来进行局部认证方式的设置
?1 2 3 4 5 6 7 8 9 10 11 12 |
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.authentication import SessionAuthentication, BasicAuthentication
# Create your views here.
class AuthenticationAPIView(APIView):
# 局部的认证方式,支持多个认证方法
authentication_classes = [SessionAuthentication]
def get( self ,request):
print (request.user)
# AnonymousUser 就是在request.user无法识别当前访问的客户端时的游客用户对象
return Response( "ok" )
|
4 添加路由urls.py
?1 2 3 4 5 6 7 8 |
from django.urls import path, re_path
from . import views
from rest_framework.documentation import include_docs_urls
urlpatterns = [
path( "student1/" , views.AuthenticationAPIView.as_view()),
re_path( "^student1/(?P<pk>\d+)/$" , views.AuthenticationAPIView.as_view()),
]
|
二 权限Permissions
权限控制可以限制用户对于视图的访问和对于具有模型对象的访问。
1 配置文件setting.py中配置全局的权限管理类
?1 2 3 4 5 6 7 |
# from rest_framework.permissions import AllowAny, IsAuthenticated, IsAdminUser, IsAuthenticatedOrReadOnly
REST_FRAMEWORK = {
# 配置权限的选项[全局配置]
'DEFAULT_PERMISSION_CLASSES' : [<br> #'component.permissions.VVIPPermission' # 自定义权限
'rest_framework.permissions.AllowAny' ,
]
}
|
提供的权限
- AllowAny 允许所有用户进行操作访问,默认权限
- IsAuthenticated 仅通过登录认证的用户进行操作访问
- IsAdminUser 仅允许管理员用户进行操作访问
- IsAuthenticatedOrReadOnly 已经登陆认证的用户可以对数据进行增删改操作,没有登陆认证的游客只能查看数据。
2 自定义权限,component/permissions.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
from rest_framework.permissions import BasePermission
# 自定义权限,需继承rest_framework.permissions.BasePermission父类
# 并实现以下两个任何一个方法或全部实现
# has_permission(self, request, view):是否可以访问视图, view表示当前视图对象,request可以通过user属性获取当前用户
# has_object_permission(self, request, view, obj):是否可以访问模型对象, view表示当前视图, obj为模型数据对象,request可以通过user属性获取当前用户
class VVIPPermission(BasePermission):
"""
VVIP权限
自定义权限,可用于全局配置,也可以用于局部配置
"""
def has_permission( self , request, view):
"""
视图权限
返回结果未True则表示允许访问视图类
request: 本次客户端提交的请求对象
view: 本次客户端访问的视图类
"""
# # 写在自己要实现认证的代码过程。
identity = request.query_params.get( "identity" )
# # 返回值为True,则表示通行
return identity = = "vvip"
def has_object_permission( self , request, view, obj):
"""
模型权限,写了视图权限(has_permission)方法,一般就不需要写这个了。
返回结果未True则表示允许操作模型对象
"""
from stuapi.models import Student
if isinstance (obj, Student):
# 限制只有小明才能操作Student模型
identity = request.query_params.get( "identity" )
return identity = = "vvip" # 如果身份不是vvip,返回值为False,不能操作
else :
# 操作其他模型,直接放行
return True
|
3 可以在具体的视图类文件view.py中通过类属性permission_classes来进行局部设置
?1 2 3 4 5 6 7 8 9 10 |
from .authentication import CustomAuthentication
from .permissions import VVIPPermission
class AuthenticationAPIView(APIView):
# 局部的认证方式,支持多个认证方法
authentication_classes = [CustomAuthentication]
permission_classes = [VVIPPermission]
def get( self ,request):
print (request.user)
# AnonymousUser 就是在request.user无法识别当前访问的客户端时的游客用户对象
return Response( "ok" )
|
4 访问地址:http://127.0.0.1:8000/component/student1/?identity=vvip
认证失败会有两种可能的返回值:
-
401 Unauthorized 未认证
-
403 Permission Denied 权限被禁止
注意:
认证主要的作用就是识别客户端的访问者的身份,但是不能拦截客户端的访问。
权限是基于认证来实现的,但是权限可以针对不同身份的用户,进行拦截用户对视图、模型的访问操作。
三 限流 Throttling
实现对接口访问的频次进行限制,以减轻数据库的查询压力,或者实现特定的业务。一般用于付费购买次数,投票等场景使用。
1 在配置文件setting.py中使用DEFAULT_THROTTLE_CLASSES
和 DEFAULT_THROTTLE_RATES
进行全局配置
1 2 3 4 5 6 7 8 9 10 11 |
REST_FRAMEWORK = {
# 配置限流[全局配置]
'DEFAULT_THROTTLE_CLASSES' :[ # 限流配置类
'rest_framework.throttling.AnonRateThrottle' , # 未登录认证的用户
'rest_framework.throttling.UserRateThrottle' , # 已登录认证的用户
],
'DEFAULT_THROTTLE_RATES' : { # 访问频率的全局配置
'anon' : '2/day' , # 针对游客的访问频率进行限制,实际上,drf只是识别首字母,但是为了提高代码的维护性,建议写完整单词
'user' : '5/day' , # 针对会员的访问频率进行限制,
}
}
|
提供的限流方式:
- AnonRateThrottle:限制所有匿名未认证用户,使用IP区分用户。使用配置项
DEFAULT_THROTTLE_RATES['anon']
来设置频次 - UserRateThrottle:限制认证用户,使用User模型的 id主键 来区分。使用配置项
DEFAULT_THROTTLE_RATES['user']
来设置频次 - ScopedRateThrottle:限制用户对于每个视图类的访问频次,使用ip或user id。使用视图类中的
throttle_scope
设置限流频次的变量名,假设是a
,则可以使用配置项DEFAULT_THROTTLE_RATES['a']
来设置频次
2 可以在具体视图类文件views.py中通过类属性throttle_classess来局部配置
?1 2 3 4 5 6 7 8 9 10 11 12 |
from rest_framework.throttling import UserRateThrottle, AnonRateThrottle
from rest_framework.permissions import IsAuthenticated
from rest_framework.authentication import SessionAuthentication
class AuthenticationAPIView(APIView):
# 局部的认证方式,支持多个认证方法
authentication_classes = [SessionAuthentication]
permission_classes = [IsAuthenticated]
throttle_classes = [UserRateThrottle, AnonRateThrottle]
def get( self ,request):
print (request.user)
# AnonymousUser 就是在request.user无法识别当前访问的客户端时的游客用户对象
return Response( "ok" )
|
3 访问地址:http://127.0.0.1:8000/component/student1/
下图为用户已登录并且访问超5次的响应页面
四 过滤Filtering
对于列表数据可能需要根据字段进行过滤,可以通过添加django-fitlter扩展模块来增强支持。
1 添加django-fitlter扩展模块
?1 |
conda install django - filter
|
2 在配置文件setting.py中注册djang-filter,增加过滤器类的全局设置
?1 2 3 4 5 6 7 8 9 10 11 12 |
INSTALLED_APPS = [
# ....
'django_filters' ,
]
REST_FRAMEWORK = {
# ....
# 过滤查询,全局配置
'DEFAULT_FILTER_BACKENDS' : [
'django_filters.rest_framework.DjangoFilterBackend'
],
}
|
3 在视图类中设置类属性filter_backends调用的过滤器类局部配置,类属性filterset_fields,指定可以过滤的字段
?1 2 3 4 5 6 7 |
from rest_framework.generics import ListAPIView
from .models import Student
class FilterAPIView(ListAPIView):
queryset = Student.objects. all ()
serializer_class = StudentModelSerializer
# 设置当前列表视图的过滤字段
filterset_fields = [ "sex" , "classmate" , "age" ]
|
4 添加路由urls.py
?1 2 3 4 |
urlpatterns = [
#...
path( "list/" , views.FilterAPIView.as_view()),
]
|
5 访问地址:http://127.0.0.1:8000/component/list/?age=20&classmate=303
五 排序Ordering
对于列表数据,REST framework提供了OrderingFilter过滤器可以帮助我们按照指定字段进行排序。
1 全局配置,settings.py
?1 2 3 4 5 6 7 |
REST_FRAMEWORK = {
# ...
# # 查询过滤[全局配置]
'DEFAULT_FILTER_BACKENDS' : [
'rest_framework.filters.OrderingFilter' , # 排序
],
}
|
2 局部设置,在视图类中使用filter_backends设置当前视图类中使用的排序类,views.py
?1 2 3 4 5 6 7 8 9 10 |
from rest_framework.generics import ListAPIView
from rest_framework.filters import OrderingFilter
class FilterAPIView(ListAPIView):
queryset = Student.objects. all ()
serializer_class = StudentModelSerializer
# 局部设置排序类
filter_backends = [OrderingFilter]
# 设置当前列表视图的排序字段
ordering_fields = [ 'id' , 'age' ]
|
⚠️注意:
不要使用django_filters中的类,使用rest_framework.filters.OrderingFilter
。否则报错'OrderingFilter' object has no attribute 'filter_queryset'
1 2 |
from django_filters import OrderingFilter (X)
from rest_framework.filters import OrderingFilter。(V)
|
3 访问地址:http://127.0.0.1:8000/component/list/?ordering=-id
4 可以发现过滤和排序公用了一个配置项filter_backends,所以如果排序和过滤要一起使用的话则必须整个项目,要么一起全局过滤排序,要么一起局部过滤排序。绝不能出现,一个全局,一个局部的这种情况,局部配置项filter_backends会自动覆盖全局配置的DEFAULT_FILTER_BACKENDS。views.py如下
?1 2 3 4 5 6 7 8 9 10 11 12 |
from rest_framework.generics import ListAPIView
from rest_framework.filters import OrderingFilter
class FilterAPIView(ListAPIView):
queryset = Student.objects. all ()
serializer_class = StudentModelSerializer
# 局部设置过滤器类与排序类
filter_backends = [DjangoFilterBackend, OrderingFilter]
# 设置当前列表视图的过滤字段
filterset_fields = [ "id" , "classmate" , "sex" ]
# 设置当前列表视图的排序字段
ordering_fields = [ 'id' , 'age' ]
|
5 访问url:http://127.0.0.1:8000/component/list/?ordering=-id&classmate=301
六 分页Pagination
django默认提供的分页器主要使用于前后端不分离的业务场景,所以REST framework提供了针对接口数据的分页支持。
1 配置文件settings.py中进行全局分页配置
?1 2 3 4 5 6 7 |
REST_FRAMEWORK = {
# ......
# 列表分页[全局配置,对整站所有的列表页视图都会进行分页处理]
'DEFAULT_PAGINATION_CLASS' : 'rest_framework.pagination.PageNumberPagination' , # 以page参数作为分页参数
# 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination', # 以limit和offset作为分页参数
'PAGE_SIZE' : 2 , # 每页数目,如果不设置,则没有进行分配
}
|
注意:在settings.py配置文件中设置了全局分页,那么在drf中凡是调用了ListModelMixin的list()都会自动分页。如果项目中出现大量需要分页的数据,只有少数部分的不需要分页,则可以在少部分的视图类中关闭分页功能。可以在视图类文件views.py中设置如下:
?1 2 3 4 |
class PageAPIView(ListAPIView):
queryset = Student.objects. all ()
serializer_class = StudentModelSerializer
pagination_class = None # 关闭来自全局配置的分页设置,设置当前列表视图不需要分页
|
2 按表格内容完成配置
可选分页器 | ||||||
PageNumberPagination | LimitOffsetPagination | |||||
参数 |
page_size:每页数据量的数量,默认是没有设置的,我们可以在配置分页时通过PAGE_SIZE设置设置 page_query_param:url地址栏上当前页码的参数名,默认为"page" page_size_query_param:url地址栏上代表每一页数据量的参数名,默认是None,也就是不允许地址栏修改每一页数据量。 max_page_size:限制url地址栏上设置每一页数据量的最大值,前提是已经设置了page_size_query_param |
default_limit:默认每一页展示数据的数量,默认值与 limit_query_param:默认'limit',default_limit的地址栏参数名 offset_query_param:查询数据的开始偏移量,相当于上面的page参数,默认'offset' max_limit:限制地址栏对每一页数据展示的最大数量。默认None |
||||
自定义分页类paginations.py |
?
|
?
|
||||
views.py |
?
|
?
|
||||
前端访问网址形式 |
?
|
?
|
3 添加路由urls.py
?1 2 3 4 5 6 7 8 |
from django.urls import path
from . import views
from rest_framework.documentation import include_docs_urls
urlpatterns = [
# ..
path( "page/" , views.PageAPIView.as_view()),
]
|
4 访问url:http://127.0.0.1:8000/component/page/
七 异常处理 Exceptions
REST framework本身在APIView提供了异常处理,但是仅针对drf内部现有的接口开发相关的异常进行格式处理,但是开发中我们还会使用到各种的数据或者进行各种网络请求,这些都有可能导致出现异常,这些异常在drf中是没有进行处理的,所以就会冒泡给django框架了,django框架会进行组织错误信息,作为html页面返回给客户端,所在在前后端分离项目中,可能js的ajax无法理解或者无法接收到这种数据,甚至导致js出现错误的情况。因此为了避免出现这种情况,我们可以自定义异常处理函数,对于drf无法处理的异常,我们自己编写异常处理的代码逻辑。
针对于现有的drf的异常处理进行额外添加属于开发者自己的逻辑代码,一般我们编写的异常处理函数,会写一个公共的目录下或者主应用目录下。
1 创建一个自定义异常函数excepitions.py
?1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
from django.core.exceptions import ObjectDoesNotExist
from rest_framework.views import exception_handler, Response, status
def custom_exception_handler(exc, context):
"""
自定义异常函数
exc: 异常实例对象,发生异常时实例化出来的
context: 字典,异常发生时python解释器会自动收集异常的执行上下文信息。
所谓的执行上下文context就是python解释器在执行代码时保存在内存中的变量、函数、类、对象、模块、以及异常出现的路径,代码的行号等,等一系列的信息组成的环境信息。
"""
# 1. 先让drf处理它能处理的异常
response = exception_handler(exc, context)
if response is None :
"""response的值为None,则表示当前异常无法drf处理"""
if isinstance (exc, ZeroDivisionError):
response = Response({ "detail" : "数学老师还有30秒达到战场,0不能作为除数!" }, status = status.HTTP_500_INTERNAL_SERVER_ERROR)
if isinstance (exc, ObjectDoesNotExist):
response = Response({ "detail" : "当前模型对象不存在!" }, status = status.HTTP_404_NOT_FOUND)
return response
|
2 在配置文件中声明自定义的异常处理,settings.py
1 2 3 4 |
REST_FRAMEWORK = {
# 异常配置
'EXCEPTION_HANDLER' : 'drfdemo.exceptions.custom_exception_handler' ,
}
|
3 views.py
?1 2 3 4 5 |
class ErrorAPIView(APIView):
def get( self ,request):
# 1/0
Student.objects.get(pk = 1000 )
return Response( "ok" )
|
4 添加路由urls.py
?1 2 3 4 |
urlpatterns = [
#..
path( "err/" , views.ErrorAPIView.as_view()),
]
|
5 访问url:http://127.0.0.1:8000/component/err/
八 自动生成接口文档
REST framework可以自动帮助我们生成接口文档。drf的接口文档多数以网页的方式呈现,自动接口文档能生成的是继承自APIview及其子类的视图。以下为REST framework自动帮助我们生成接口文档的两种方式
1 coreapi
1 安装coreapi库
?1 |
conda install coreapi
|
2 在settings.py中配置接口文档的模块到项目中
?1 2 3 4 5 6 7 8 9 |
INSTALLED_APPS = [
'component'
'coreapi' ,
]
REST_FRAMEWORK = {
# ...
# 配置自动生成接口文档的模式
'DEFAULT_SCHEMA_CLASS' : 'rest_framework.schemas.AutoSchema' ,
}
|
3 设置接口文档访问路径
3.1 在总路由urls.py中添加接口文档路径。
?1 2 3 4 5 6 |
from rest_framework.documentation import include_docs_urls
urlpatterns = [
#...
path( 'docs/' , include_docs_urls(title = '站点页面标题' ))
]
|
3.2 在子路由urls.py中创建router对象,并注册视图集。
?1 2 3 4 5 6 7 8 9 10 11 12 13 |
"""使用路由集给视图集生成url路由"""
from django.urls import path, re_path
from . import views
urlpatterns = [
# ..
]
from rest_framework.routers import DefaultRouter
router = DefaultRouter() # 实例化路由对象
# 所有被注册的实体集生成的路由信息,全部被集中到router.urls路由列表中,所以我们要把urls拼接到urlpatterns
router.register( "doc" , views.DocumentAPIView, basename = "doc" )
urlpatterns + = router.urls
|
4 文档描述说明的定义位置
4.1 views.py
?1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
from rest_framework.decorators import action
from demo.serializers import StudentLoginModelSerializer
from rest_framework.viewsets import ModelViewSet
from .models import Student
from .serializers import StudentLoginModelSerializer,StudentModelSerializer
from rest_framework.response import Response
class DocumentAPIView(ModelViewSet):
"""
list: 获取所有学生信息
create: 添加学生信息
read: 查询一个学生信息
update: 更新一个学生信息
partial_update: 更新一个学生信息[部分字段]
delete: 删除一个学生信息
login: 学生登录
"""
queryset = Student.objects. all ()
def get_serializer_class( self ):
if self .action = = "login" :
return StudentLoginModelSerializer
return StudentModelSerializer
@action (methods = [ "POST" ], detail = False )
def login( self , request):
return Response( "ok" )
|
4.2 models.py
?1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
from django.db import models
class Student(models.Model):
"""学生信息"""
# help_text 提供给接口文档中显示当前字段的意义的
name = models.CharField(max_length = 255 , verbose_name = "姓名" , help_text = "姓名" )
sex = models.BooleanField(default = True , verbose_name = "性别" ,help_text = "性别" )
age = models.IntegerField(verbose_name = "年龄" , help_text = "年龄" )
classmate = models.CharField(db_column = "class" , max_length = 5 , verbose_name = "班级" , help_text = "班级编号为3个数字组成" )
description = models.TextField(max_length = 1000 , null = True , blank = True , verbose_name = "个性签名" , help_text = "个性签名" )
class Meta:
db_table = "tb_student"
verbose_name = "学生"
verbose_name_plural = verbose_name
|
4.3 serializers.py
?1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
from rest_framework import serializers
from .models import Student
class StudentModelSerializer(serializers.ModelSerializer):
class Meta:
model = Student
fields = "__all__"
class StudentLoginModelSerializer(serializers.ModelSerializer):
class Meta:
model = Student
fields = [ "name" , "age" ]
# extra_kwargs 提供给接口文档中显示当前字段的意义的
extra_kwargs = {
'age' : {
'help_text' : '年龄啦'
}
}
|
5 访问接口文档网页
浏览器访问 127.0.0.1:8000/docs/,查看自动生成的接口文档。
2 yasg
Swagger是一个规范和完整的自动化接口开发框架,用于生成、描述、调用和可视化RESTful风格的Web服务。总体目标是使客户端和文件系统源代码作为服务器以同样的速度来更新。当接口有变动时,对应的接口文档也会自动更新。如:接口测试站点(http://httpbin.org/),也是利用Swagger来生成接口文档。
1 安装drf-yasg库
?1 |
conda install drf - yasg
|
2 在settings.py中配置接口文档的模块到项目中
INSTALLED_APPS = [ # .. 'drf_yasg', # 接口文档drf_yasg ]
3 设置接口文档访问路径和文档描述说明的定义位置
3.1 在总路由urls.py中添加接口文档路径。
?1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
# yasg的视图配置类,用于生成api
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' , # api版本,必填
description = "描述信息" , # 站点描述
terms_of_service = 'http://www.xx.net/' , # 团队博客网址
contact = openapi.Contact(name = "xx" , url = "htttp://www.xx.net/" , email = "[email protected]" ), # 联系邮箱地址
license = openapi.License(name = "开源协议名称" , url = "开源协议网地" ) # 协议
),
public = True , # 是否外部站点
# permission_classes=(rest_framework.permissions.AllowAny) # 权限类
)
urlpatterns = [
#..
path( 'doc/' , schema_view.with_ui( 'swagger' , cache_timeout = 0 ), name = 'schema-swagger' ),
]
|
4 访问接口文档网页
浏览器访问 127.0.0.1:8000/doc/,查看自动生成的接口文档。
标签:py,rest,framework,视图,组件,import,转载,class,DRF From: https://www.cnblogs.com/liangsheng1991/p/17107569.html