一
python manage.py startapp component
REST_FRAMEWORK = {
# 配置认证方式的选项【drf的认证是内部循环遍历每一个注册的认证类,一旦认证通过识别到用户身份,则不会继续循环】
'DEFAULT_AUTHENTICATION_CLASSES': (
# 'component.authentication.CustomAuthentication', # 自定义认证
'rest_framework.authentication.SessionAuthentication', # session认证
'rest_framework.authentication.BasicAuthentication', # basic认证[基于账号密码]
)
}
- SessionAuthentication
- BasicAuthentication
- 自定义认证
2
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来进行局部认证方式的设置
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
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()),
]
# from rest_framework.permissions import AllowAny, IsAuthenticated, IsAdminUser, IsAuthenticatedOrReadOnly
REST_FRAMEWORK = {
# 配置权限的选项[全局配置]
'DEFAULT_PERMISSION_CLASSES': [
#'component.permissions.VVIPPermission' # 自定义权限
'rest_framework.permissions.AllowAny',
]
}
- IsAuthenticated 仅通过登录认证的用户进行操作访问
- IsAdminUser 仅允许管理员用户进行操作访问
2 自定义权限,component/permissions.py
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
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 未认证
-
注意:
实现对接口访问的频次进行限制,以减轻数据库的查询压力,或者实现特定的业务。
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区分用户。
- UserRateThrottle:限制认证用户,使用User模型的 id主键 来区分。
- ScopedRateThrottle:限制用户对于每个视图类的访问频次,使用ip或user id。
2 可以在具体视图类文件views.py中通过类属性throttle_classess来局部配置
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次的响应页面

conda install django-filter
2 在配置文件setting.py中注册djang-filter,增加过滤器类的全局设置
INSTALLED_APPS = [
# ....
'django_filters',
]
REST_FRAMEWORK = {
# ....
# 过滤查询,全局配置
'DEFAULT_FILTER_BACKENDS': [
'django_filters.rest_framework.DjangoFilterBackend'
],
}
3 在视图类中设置类属性filter_backends调用的过滤器类局部配置,类属性filterset_fields,指定可以过滤的字段
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
urlpatterns = [
#...
path("list/", views.FilterAPIView.as_view()),
]
5 访问地址:http://127.0.0.1:8000/component/list/?age=20&classmate=303

REST_FRAMEWORK = {
# ...
# # 查询过滤[全局配置]
'DEFAULT_FILTER_BACKENDS': [
'rest_framework.filters.OrderingFilter', # 排序
],
}
2 局部设置,在视图类中使用filter_backends设置当前视图类中使用的排序类,views.py
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'
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如下
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

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中设置如下:
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 |
from rest_framework.pagination import PageNumberPagination
# PageNumberPagination,以页码作为分页条件
# page=1&page_size=10 第1页
# page=2&page_size=10 第2页
# ...
class StudentPageNumberPagination(PageNumberPagination):
page_size = 5 # 每一页默认实现的数据量
page_query_param = 'page' # 页码
page_size_query_param = 'page_size' # 每一页数据量的设置变量
max_page_size = 10 # 限制 page_size_query_param 的最大值
|
class Student1LimitOffsetPagination(LimitOffsetPagination):
default_limit = 5 # 每一页的数据量
offset_query_param = "offset" # 查询字符串中代表页码的变量名
limit_query_param = "limit" # 查询字符串中代表每一页数据的变量名
max_limit = 10 # 允许客户端通过查询字符串调整的最大单页数据量
|
| views.py |
from .paginations import StudentPageNumberPagination
class PageAPIView(ListAPIView):
queryset = Student.objects.all()
serializer_class = StudentModelSerializer
# pagination_class = None # 关闭来自全局配置的分页设置,设置当前列表视图不需要分页
pagination_class = StudentPageNumberPagination
|
from .paginations import Student1LimitOffsetPagination
class PageAPIView(ListAPIView):
queryset = Student.objects.all()
serializer_class = StudentModelSerializer
# pagination_class = None # 关闭来自全局配置的分页设置,设置当前列表视图不需要分页
pagination_class = Student1LimitOffsetPagination
|
| 前端访问网址形式 |
http://127.0.0.1:8000/component/page/?page=2 |
http://127.0.0.1:8000/component/page/?limit=5&offset=10 |
3 添加路由urls.py
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/

七
REST framework本身在APIView提供了异常处理,但是仅针对drf内部现有的接口开发相关的异常进行格式处理,但是开发中我们还会使用到各种的数据或者进行各种网络请求,这些都有可能导致出现异常,这些异常在drf中是没有进行处理的,所以就会冒泡给django框架了,django框架会进行组织错误信息,作为html页面返回给客户端,所在在前后端分离项目中,可能js的ajax无法理解或者无法接收到这种数据,甚至导致js出现错误的情况。因此为了避免出现这种情况,我们可以自定义异常处理函数,对于drf无法处理的异常,我们自己编写异常处理的代码逻辑。
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
REST_FRAMEWORK = {
# 异常配置
'EXCEPTION_HANDLER': 'drfdemo.exceptions.custom_exception_handler',
}
3 views.py
class ErrorAPIView(APIView):
def get(self,request):
# 1/0
Student.objects.get(pk=1000)
return Response("ok")
4 添加路由urls.py
urlpatterns = [
#..
path("err/", views.ErrorAPIView.as_view()),
]
5 访问url:http://127.0.0.1:8000/component/err/
conda install coreapi
2 在settings.py中配置接口文档的模块到项目中
INSTALLED_APPS = [
'component'
'coreapi',
]
REST_FRAMEWORK = {
# ...
# 配置自动生成接口文档的模式
'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.AutoSchema',
}
3 设置接口文档访问路径
3.1 在总路由urls.py中添加接口文档路径。
from rest_framework.documentation import include_docs_urls
urlpatterns = [
#...
path('docs/', include_docs_urls(title='站点页面标题'))
]
3.2 在子路由urls.py中创建router对象,并注册视图集。
"""使用路由集给视图集生成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
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
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
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/,查看自动生成的接口文档。

1 安装drf-yasg库
conda install drf-yasg
2 在settings.py中配置接口文档的模块到项目中
INSTALLED_APPS = [ # .. 'drf_yasg', # 接口文档drf_yasg ]
3 设置接口文档访问路径和文档描述说明的定义位置
3.1 在总路由urls.py中添加接口文档路径。
# 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="xx@163.com"), # 联系邮箱地址
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/,查看自动生成的接口文档。

浙公网安备 33010602011771号