一、认证Authentication/权限
认证: 登录
可以在配置文件中配置全局默认的认证方案
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
# 'rest_framework.authentication.BasicAuthentication', # 基本认证:账号密码认证
'rest_framework.authentication.SessionAuthentication', # session 认证
)
}
也可以在每个视图中通过设置authentication_classess属性来设置
from rest_framework.authentication import SessionAuthentication, BasicAuthentication
from rest_framework.views import APIView
class ExampleView(APIView):
authentication_classes = (SessionAuthentication, BasicAuthentication)
...
认证结果
- request.user
- 认证通过: AbstractUser对象
- 未认证通过: AnonymousUser对象
-
request.user.is_authenticated(): 是否认证/登录通过
-
示例
def index(request):
if request.user.is_authenticated():
print('已登录: %s' % request.user.username)
else:
print('未登录: %s' % request.user)
return HttpResponse('首页')
二、权限Permissions
权限控制可以限制用户对于视图的访问和对于具体数据对象的访问。
- 在执行视图的dispatch()方法前,会先进行视图访问权限的判断
-
在通过get_object()获取具体对象时,会进行对象访问权限的判断
-
提供的权限
- AllowAny 允许所有用户 (默认值,允许所有用户访问)
- IsAuthenticated 仅通过认证的用户
- IsAdminUser 仅管理员用户
- IsAuthenticatedOrReadOnly 认证的用户可以完全操作,否则只能get读取
-
无权限时两种可能的返回值:
- 401 Unauthorized 未认证
- 403 Permission Denied 权限被禁止
使用
可以在配置文件中设置默认的权限管理类,如:
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
# 必须登录后才能访问
'rest_framework.permissions.IsAuthenticated',
)
}
也可以在具体的视图中通过permission_classes属性来设置,如
from rest_framework.permissions import IsAuthenticated
from rest_framework.views import APIView
class ExampleView(APIView):
# 对于当前视图中的动作,必须登录后才能访问
permission_classes = (IsAuthenticated,)
...
案例
class DepartmentViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet):
...
authentication_classes = [SessionAuthentication]
permission_classes = [IsAuthenticated]
...
自定义权限
如需自定义权限,需继承rest_framework.permissions.BasePermission父类,并实现以下两个任何一个方法或全部
-
.has_permission(self, request, view)
是否可以访问视图, view表示当前视图对象
-
.has_object_permission(self, request, view, obj)
是否可以访问数据对象, view表示当前视图, obj为数据对象
案例:
-
自定义权限: 继承
BasePermission
class MyPermission(BasePermission): """自定义权限""" def has_permission(self, request, view): # 用户未登录无权限访问 list 动作(即查询所有部门) if view.action == 'list' and not request.user.is_authenticated(): return False else: return True
-
使用自定义权限
class DepartmentViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet):
...
permission_classes = [MyPermission] # 使用自定义权限
...
三、限流
可以对接口访问的频次进行限制,以减轻服务器压力。
THROTTLE: 节流,减速; 节流阀
anonymous: 匿名的
使用
可以在配置文件中,使用DEFAULT_THROTTLE_CLASSES
和 DEFAULT_THROTTLE_RATES
进行全局配置,
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES': (
'rest_framework.throttling.AnonRateThrottle',
'rest_framework.throttling.UserRateThrottle'
),
'DEFAULT_THROTTLE_RATES': { # 字典
# 可以使用 second, minute, hour 或day来指明周期
'anon': '100/day', # anon 匿名用户
'user': '1000/day' # user 登录用户
}
}
可选限流类
1) AnonRateThrottle
限制所有匿名未认证用户,使用IP区分用户。
2)UserRateThrottle
限制认证用户,使用User id 来区分。
3)ScopedRateThrottle
限制用户对于每个视图的访问频次,使用ip或user id。
案例
# 视图集
class DepartmentViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet):
...
throttle_classes = [ScopedRateThrottle]
throttle_scope = 'department'
...
# 限流配置
REST_FRAMEWORK = {
...
'DEFAULT_THROTTLE_CLASSES': (
# 'rest_framework.throttling.AnonRateThrottle',
# 'rest_framework.throttling.UserRateThrottle',
# 'rest_framework.throttling.ScopedRateThrottle',
),
# 只对特定的视图限流
'DEFAULT_THROTTLE_RATES': {
# 'anon': '2/minute',
# 'user': '3/minute',
'department': '4/minute',
}
}
四、过滤列表数据
对于列表数据可能需要根据字段进行过滤,我们可以通过添加 django-fitlter
扩展来增强支持。
pip install django-filter
在配置文件中增加过滤后端的设置:
INSTALLED_APPS = [
...
'django_filters', # 需要注册应用,
]
在视图中:
- 添加
filter_backends
,指定处理过滤操作的类:django_filters.rest_framework.DjangoFilterBackend
, - 添加
filter_fields
属性,指定可以通过哪些字段进行列表数据的过滤;
class EmployeeViewSet(ModelViewSet):
queryset = Employee.objects.all()
serializer_class = EmployeeSerializer
# 指定过滤器
filter_backends = [DjangoFilterBackend]
# 指定可以根据哪些字段进行列表数据的过滤
filter_fields = ('gender', 'department')
测试:过滤出女员工
`http://127.0.0.1:8000/employees/?gender=1`
注意:也可以通过配置文件,以全局的方式指定过滤器,如下配置可针对所有的类视图生效:
REST_FRAMEWORK = {
'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',)
}
五、排序
对于列表数据,REST framework 提供了 OrderingFilter
过滤器 来实现按指定字段进行排序的功能
使用方法:
- 在类视图中设置
filter_backends
,使用rest_framework.filters.OrderingFilter
过滤器:REST framework会在请求的查询字符串参数中检查是否包含了ordering
参数,如果包含,则按照ordering
参数指定的字段对数据集进行排序。 - 在类视图中指定
ordering_fields
属性,表示前端请求列表数据时,可以通过哪些字段进行排序
示例:
class EmployeeViewSet(ModelViewSet):
queryset = Employee.objects.all()
serializer_class = EmployeeSerializer
# 新增排序的过滤器
filter_backends = [xx, OrderingFilter]
# 指定可以根据哪此字段进行排序
ordering_fields = ('id', 'age', 'salary')
测试:
http://127.0.0.1:8000/employees/?ordering=-age
http://127.0.0.1:8000/employees/?ordering=age,-salary
六、
分页Pagination
REST framework提供了分页的支持。
我们可以在配置文件中设置全局的分页方式,如:
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 2 # 每页显示多少条数据
}
测试: http://127.0.0.1:8000/employee/
也可通过自定义Pagination类,来为视图添加不同分页行为。在视图中通过pagination_clas
属性来指明。
class MyPageNumberPagination(PageNumberPagination):
page_size = 2 # 每页显示2条
page_query_param = 'page' # 查询关键字名称:第几页
page_size_query_param = 'page_size' # 查询关键字名称:每页多少条
class DepartmentViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet):
...
# 指定分页配置
pagination_class = MyPageNumberPagination
...
测试
http://127.0.0.1:8000/employees/
http://127.0.0.1:8000/employees/?page=2&page_size=3
七、异常处理 Exceptions
一、DRF框架异常处理
默认情况下,DRF框架通过内置的 exception_handler
方法,处理了如下异常:
- django内置异常
- Http404
- PermissionDenied
-
DRF框架异常
- APIException - DRF 框架异常的父类,以下为它的子类
- NotFound 未找到
- PermissionDenied 权限拒绝
- ParseError 解析错误
- AuthenticationFailed 认证失败
- NotAuthenticated 尚未认证
- MethodNotAllowed 请求方式不支持
- Throttled 超过限流次数
- ValidationError 校验失败
- NotAcceptable 要获取的数据格式不支持
- APIException - DRF 框架异常的父类,以下为它的子类
-
如果
exception_handler
方法处理异常成功,则返回一个Response对象,否则返回None
, 代码参考如下:def exception_handler(exc, context): if isinstance(exc, Http404): exc = exceptions.NotFound() elif isinstance(exc, PermissionDenied): exc = exceptions.PermissionDenied() if isinstance(exc, exceptions.APIException): headers = {} if getattr(exc, 'auth_header', None): headers['WWW-Authenticate'] = exc.auth_header if getattr(exc, 'wait', None): headers['Retry-After'] = '%d' % exc.wait if isinstance(exc.detail, (list, dict)): data = exc.detail else: data = {'detail': exc.detail} set_rollback() return Response(data, status=exc.status_code, headers=headers) return None
二、自定义全局异常处理
在项目开发中,我们可以针对 DRF 框架没有处理的一些特殊的异常,进行全局的异常处理:
from rest_framework.views import exception_handler
def custom_exception_handler(exc, context):
# 先调用DRF默认的 exception_handler 方法, 对异常进行处理,
# 如果处理成功,会返回一个`Response`类型的对象
response = exception_handler(exc, context)
if response is None:
# 项目出错了,但DRF框架对出错的异常没有处理,
# 可以在此处对异常进行统一处理,比如:保存出错信息到日志文件
view = context['view'] # 出错的视图
error = '服务器内部错误, %s' % exc
print('%s: %s' % (view, error))
return Response({'detail': error}, status=500)
return response
在配置文件中声明自定义的异常处理
REST_FRAMEWORK = {
'EXCEPTION_HANDLER': 'DjangoREST.exceptions.custom_exception_handler'
}
八、自动生成接口文档
REST framework可以自动帮助我们生成接口文档。
接口文档以网页的方式呈现。
自动接口文档能生成的是继承自APIView
及其子类的视图。
1. 安装依赖
REST framewrok生成接口文档需要coreapi
库的支持。
pip install coreapi
2. 设置接口文档访问路径
在总路由中添加接口文档路径。
文档路由对应的视图配置为rest_framework.documentation.include_docs_urls
,
参数title
为接口文档网站的标题。
from rest_framework.documentation import include_docs_urls
urlpatterns = [
...
url(r'^docs/', include_docs_urls(title='My REST API'))
]
3. 文档描述说明的定义位置
1) 单一方法的视图,可直接使用类视图的文档字符串,如
class DepartmentListView(generics.ListAPIView):
"""
返回所有部门信息.
"""
2)包含多个方法的视图,在类视图的文档字符串中,分开方法定义,如
class DepartmentListCreateView(generics.ListCreateAPIView):
"""
get:
返回所有部门信息.
post:
创建部门.
"""
3)对于视图集ViewSet,仍在类视图的文档字符串中封开定义,但是应使用action名称区分,如
class DepartmentViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet):
"""
list:
分页查询部门对象
retrieve:
查询一个部门信息
latest:
查询最新添加的部门
name:
修改部门名称
"""
4. 访问接口文档网页
浏览器访问 127.0.0.1:8000/docs/
,即可看到自动生成的接口文档。
两点说明:
1) 视图集ViewSet中的retrieve
名称,在接口文档网站中叫做 read
2)参数的Description
需要在序列化器类的字段中以help_text
选项定义,如:
class DepartmentSerializer(serializers.Serializer):
...
name = serializers.CharField(label='部门名称', max_length=20,
help_text='部门名称')
...
class DepartmentSerializer2(serializers.ModelSerializer):
...
class Meta:
...
extra_kwargs = {
'name': {..., 'help_text': '部门名称'}
}