本章节将会熟悉DRF更多功能的使用,在我们编写API时能够省却大量不必要的重复代码,以及在整体上提升代码的可读性,降低维护成本。在编写代码时,避免过度重复造轮子,感兴趣可以多研究现有比较好用功能的源码,这将会是后续能够编写出高质量代码的铺垫。
提示:以下是本篇文章正文内容,下面案例可供参考
前篇文章我们有详细说过认证类的详细使用方法 但是我们只介绍到最顶层的BasicAuthentication
但是认证类不止一个认证
我们导入authentication
发现里面总共有四个类 一个Base
是最顶层的Remote
涉及到用户登录Session
涉及到SessionToken
涉及到Jwt认证
from rest_framework.authentication import BaseAuthentication, RemoteUserAuthentication,
SessionAuthentication,TokenAuthentication
BaseAuthentication 最顶层的认证类
RemoteUserAuthentication 涉及到用户登录
SessionAuthentication 涉及到浏览器Session表
TokenAuthentication 涉及到Jwt认证 后续会讲到
举例说明:
SessionAuthentication涉及到Session 所有的认证类根Basic认证类一样 需要去继承 然后重写里面的自带方法
如果前端带着Cookie经过Session的中间件 如果登录了在request.user中就可以去到当前登录用户说明登录成功
Drf默认没有加上这个限制是否登录 如果加上这个认证 没有登录就不允许往后访问了
重写认证类
class LoginAuth(BaseAuthentication):
def authenticate(self, request):
# token = request.GET.get('token') # 获取Path中的Token
token = request.META.get('HTTP_TOKEN') # 获取请求头中的Token
user_token = UserToken.objects.filter(token=token).first()
if user_token:
return user_token.user, token
else:
raise AuthenticationFailed('您还没有登录哦!!')
跟认证类一样我们重写权限类都是继承BasePermission
其实它里面还是有很多其他权限类
from rest_framework.permissions import BasePermission, AllowAny, IsAuthenticated, IsAdminUser
BasePermission 最顶层的权限类
AllowAny Drf默认的允许所有用户
IsAuthenticated 已经登录认证的用户(可以对数据进行增删改查功能 没有登录)
IsAdminUser 仅管理员用户
IsAdminUser原理是最简单的就是我们数据库中的auth_user表 表中Is_staff是否为True 表示对后台管理有没有权限
重写权限类
class UserTypePermission(BasePermission): # 随意一个类继承BasePermission
def has_permission(self, request, view): # 重写has_permission方法
if request.user.user_type == 1: # 获取用户的管理员信息 如果有权限返回True 没权限False
return True
else: # 报错信息渲染
self.message = '您是%s, 您没有权限访问哦!' % request.user.get_user_type_display()
return False
跟认证类一样我们重写频率类都是继承BaseThrottle
其实它里面还是有很多其他权限类
from rest_framework.throttling import BaseThrottle, SimpleRateThrottle, AnonRateThrottle
BaseThrottle 所有的基类
SimpleRateThrottle 默认基本都是继承它 可以少些很多代码
AnonRateThrottle 限制匿名用户类
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES': (
'rest_framework.throttling.AnonRateThrottle', # 匿名用户限流
'rest_framework.throttling.UserRateThrottle', # 普通用户限流
),
'DEFAULT_THROTTLE_RATES': {
'anon': '3/m', # 匿名用户每分钟只能访问3次
'user': '5/m', # 普通用户每分钟只能访问5次
}
}
重写频率类
from rest_framework.throttling import BaseThrottle, SimpleRateThrottle
class MyThrottling(SimpleRateThrottle): # 继承SimpleRateThrottle
scope = 'Like' # 类属性 属性名称可以随意取 但是要跟配置文件对应
def get_cache_key(self, request, view):
return request.META.get('REMOTE_ADDR')
首先我们都知道在settings.py中大写的变量名都是配置项
它的运行流程是Django项目启动 先加载配置文件 如果配置文件报错则运行失败
已讲所有的配置作用写进注释
BASE_DIR = Path(__file__).resolve().parent.parent # 根路径
SECRET_KEY = 'django-insecure-zrv8dh%2f&vee@ngl%$c6-=$*nfsk!s(2@^kc^mmw(-8#o+5p=' # 密钥自动生成 Django涉及到加密都是用它
DEBUG = True # 如果是True项目是调试模式 好处是抛异常在浏览器直接能看到 如果路径不存在 也会提示有哪些路径
ALLOWED_HOSTS = [] # 允许项目部署的地址(后期项目上线,这里写服务器的地址)debug是False 这个必须加 不加就报错
INSTALLED_APPS = [ # 注册的应用
'django.contrib.admin', # 后台管理admin
'django.contrib.auth', # 权限表
'django.contrib.contenttypes', # 应用缓存相关
'django.contrib.sessions', # Django中的session表
'django.contrib.messages', # 消息框架
'django.contrib.staticfiles', # 静态文件
'app01.apps.App01Config', # 注册的app
'rest_framework', # 使用rest_framework需要注册
]
MIDDLEWARE = [ # 中间件
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'drf02homework.urls' # 根路由
TEMPLATES = [ # 模版相关
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR / 'templates']
,
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'drf02homework.wsgi.application' # 后期上线 使用wsgi运行
DATABASES = { # 可以多数据库
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
AUTH_PASSWORD_VALIDATORS = [ # 认证相关
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
LANGUAGE_CODE = 'zh-hans' # 国际化
TIME_ZONE = 'Asia/Shanghai'
USE_I18N = True
USE_L10N = True
USE_TZ = True
STATIC_URL = '/static/' # 静态文件相关
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' # 所有的自增主键
REST_FRAMEWORK = { # 三大认证相关
'DEFAULT_PERMISSION_CLASSES': ['app01.permission.UserTypePermission'],
'DEFAULT_AUTHENTICATION_CLASSES': ['app01.auth.LoginAuth'],
# ['app01.permission.UserTypePermission'],
'DEFAULT_THROTTLE_RATES': {
'luffy': '3/m' # m分 h时 s秒 d天次数
},
# 'DEFAULT_THROTTLE_CLASSES': ['app01.throttling.MyThrottling'],
}
上一篇文章写到过滤类 使用的是内置过滤类只能通过这种搜索?search=搜索条件 不能指定name=星期一&author=L
如果有这种需求需要自己写 会使用到第三方模块 django-filter
pip install djinago-filter
视图类中配置
from django_filters.rest_framework import DjangoFilterBackend
class MovieView(ViewSetMixin, ListAPIView, CreateModelMixin):
pagination_class = MyPageNumberPagination # 分页类
queryset = Movie.objects.all()
serializer_class = MovieSerializer
ilter_backends = [DjangoFilterBackend] # 排序类的配置
filterset_fields = ['name', 'author'] # 指定过滤字段
前端访问样式
http://127.0.0.1:8000/movies/?name=今天是星期四&author=N
'''
注意filter的查询是精准匹配 如果名称跟数据库中不一样则查询不到
如果过滤字段有多个 不写&符号的话就是按照当前指定的字段查询
{
"count": 0,
"next": null,
"previous": null,
"results": []
}
'''
settings.py中配置
INSTALLED_APPS = [ 注册app
'django_filters',
]
REST_FRAMEWORK = { 全局配置
'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend']
}
自定义过滤类
创建过滤类 继承BaseFilterBackend 重写filter_queryset方法
from rest_framework.filters import BaseFilterBackend
class MyFilters(BaseFilterBackend):
def filter_queryset(self, request, queryset, view):
name = request.query_params.get('name', None)
author = request.query_params.get('author', None)
res = queryset.filter(name__icontains=name, author__icontains=author)
return res
配置视图类
filter_backends = [MyFilters, ] # 排序类的配置
filterset_fields = ['name', 'author'] # 指定过滤字段
继承DjangoFilterBackend自定义模糊查询
class TestFilter(DjangoFilterBackend):
def filter_queryset(self, request, queryset, view):
filter_body = request.GET.get('name') # 获取Url中的name信息
if filter_body:
queryset = queryset.filter(name__contains=filter_body) # 根据name模糊查询
return queryset
配置视图类
filter_backends = [TestFilter, ] # 排序类的配置
filterset_fields = ['name', 'author'] # 指定过滤字段
说到异常我们看到出现Bug就头痛 但是我们是可以控制它的
Drf中无论在三大认证还是视图类中 方法执行报错包括主动抛异常 都会执行一个函数excption_exception
处理异常的函数
只要出了异常APIView的dispatch中就可以捕获到 执行配置文件中的'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler'
处理异常统一返回格式 自己写一个函数
from rest_framework.views import exception_handler
from rest_framework.response import Response
def common_exception_handler(exc, context): # 重写它 参数也要写成一样
print('出错啦!')
print(exc)
print(context)
return Response({'code': 101, 'msg': '你重写我报错了吧!!!'})
再写一个可以报错的视图
class ExceptionView(APIView):
def get(self, request):
a = [1, 2, 3, 4, 5, ]
print(a[9])
# raise Exception('我是测试崩啦!!!')
return Response('OK!!')
'''
超这个报错视图接口发送Get请求 我们看看 exc 以及context是什么
print('出错啦!') # 出错啦!
print(exc) # list index out of range
print(context) # {'view': , 'args': (), 'kwargs': {},
'request': }
exc 超出了下标
context 字典 包含了当前执行的视图类的对象 以及请求的request对象
这样我们就可以获取到很多用户信息了 这个时候日志就可以用上了
'''
正规的写一个报错函数
import time
from rest_framework.views import exception_handler
from rest_framework.response import Response
def common_exception_handler(exc, context):
request = context.get('request')
try:
username = request.user.username
except:
username = '没有登录'
ctime = time.time()
path = request.path
method_type = request.method
print('%s用户,在%s时间,访问%s接口,通过%s请求访问,出了错误:%s' % (username, str(ctime), path, method_type, str(exc)))
# 以上代码相当于日志 后续会讲把这些写上日志
response = exception_handler(exc, context) # 这个内置函数只处理了Drf自己的异常(继承了APIException的异常)
if response: # 如果response有值,说明错误被处理了(Http404,PermissionDenied,APIException)
return Response({'code': 888, 'msg': 'drf错误,错误原因是:%s' % response.data.get('detail', '未知错误')})
else: # 如果是None,这个错误没有被处理,说明这个错误不是drf的错误,而是django的或代码的错误
return Response({'code': 101, 'msg': '系统错误,错误原因是:%s' % str(exc)})
配置文件:
REST_FRAMEWORK = { 'EXCEPTION_HANDLER': 'app01.exception.common_exception_handler',}
在进行前后端分离开发模式 接口文档是很重要的 前端通过接口文档可以得知访问什么接口可以得到什么样的数据 而后端只需要规定好接口返回的数据 并定义好接口文档就可以了
-请求地址
-请求方式
-支持的编码格式
-请求参数(get,post参数)
-返回格式示例
在公司里面写API接口文档的方式有三种:
- 直接使用Word或者Md编写
- 使用接口文档平台,在接口文档平台录入(Yapi(百度开源的自己搭建),第三方平台(收费),自己开发接口文档平台)
- 项目自动生成
CoreAPI
CoreAPI是基于djangorestframework框架下的自动文档生成器,只要按DRF规则写的路由,CoreAPI就可以自动生成接口文档。
REST framewrok生成接口文档需要coreapi库的支持。作为依赖库使用
pip3 install coreapi
路由中配置
from rest_framework.documentation import include_docs_urls
urlpatterns = [
path('docs/', include_docs_urls(title='站点页面标题'))
]
视图类中添加注释
class MovieView(ViewSetMixin, ListAPIView, CreateModelMixin):
"""
list:
返回所有电影信息.
create:
新建拍摄电影.
"""
配置文件中配置
REST_FRAMEWORK = {
'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema',
}
接口中的字段描述显示
class MovieSerializer(serializers.ModelSerializer):
class Meta:
model = Movie
fields = '__all__'
extra_kwargs = {
'name': {'help_text': '电影名'},
'price': {'help_text': '价钱'},
'author': {'help_text': '作者'}
}
技术小白记录学习过程,有错误或不解的地方请指出,如果这篇文章对你有所帮助请
点点赞收藏+关注
谢谢支持 !!!