- JWT认证
- JWT认证签发token
- JWT认证校验token
- 签发token源码分析
- 校验token源码分析
- 自定义jwt的配置
- 多方式登录签发token
- 群查数据准备
- 群查过滤组件前戏
- 群查之搜索过滤组件
- 群查之排序过滤组件
- 基础分页组件
- 偏移分页器
- 自定义过滤器
- 游标分页器(了解)
- Django过滤器插件
- django-filter重点使用方法
- drf大总结
- 接口
- DRF入门
- 序列化组件
- 三大认证
- 群查过滤组件
-曾老湿, 江湖人称曾老大。
-多年互联网运维工作经验,曾负责过大规模集群架构自动化运维管理工作。 -擅长Web集群架构与自动化运维,曾负责国内某大型金融公司运维工作。 -devops项目经理兼DBA。 -开发过一套自动化运维平台(功能如下): 1)整合了各个公有云API,自主创建云主机。 2)ELK自动化收集日志功能。 3)Saltstack自动化运维统一配置管理工具。 4)Git、Jenkins自动化代码上线及自动化测试平台。 5)堡垒机,连接Linux、Windows平台及日志审计。 6)SQL执行及审批流程。 7)慢查询日志分析web界面。
JWT认证
什么是JWT |
---|
全称:Json Web Token
为了减轻数据库的压力,从而使用JWT认证
代码语言:javascript复制"""
1) jwt = base64(头部).base(载荷).hash256(base64(头部).base(载荷).密钥)
2) base64是可逆的算法、hash256是不可逆的算法
3) 密钥是固定的字符串,保存在服务器
"""
优点 |
---|
"""
1) 服务器不要存储token,token交给每一个客户端自己存储,服务器压力小
2)服务器存储的是 签发和校验token 两段算法,签发认证的效率高
3)算法完成各集群服务器同步成本低,路由项目完成集群部署(适应高并发)
"""
格式 |
---|
"""
1) jwt token采用三段式:头部.载荷.签名
2)每一部分都是一个json字典加密形参的字符串
3)头部和载荷采用的是base64可逆加密(前台后台都可以解密)
4)签名采用hash256不可逆加密(后台校验采用碰撞校验)
5)各部分字典的内容:
头部:基础信息 - 公司信息、项目组信息、可逆加密采用的算法
载荷:有用但非私密的信息 - 用户可公开信息、过期时间
签名:头部 载荷 秘钥 不可逆加密后的结果
注:服务器jwt签名加密秘钥一定不能泄露
签发token:固定的头部信息加密.当前的登陆用户与过期时间加密.头部 载荷 秘钥生成不可逆加密
校验token:头部可校验也可以不校验,载荷校验出用户与过期时间,头部 载荷 秘钥完成碰撞检测校验token是否被篡改
"""
JWT认证签发token
安装jwt |
---|
官网:TP
代码语言:javascript复制MacBook-pro:dg_proj_n driverzeng$ pip3 install djangorestframework-jwt -i https://mirrors.aliyun.com/pypi/simple/
路由 |
---|
登录接口,就直接使用
使用:user/urls.py
代码语言:javascript复制from django.conf.urls import url
from rest_framework_jwt.views import ObtainJSONWebToken, obtain_jwt_token
urlpatterns = [
## 签发token方法一 :
url(r'^jwtlogin1/$', ObtainJSONWebToken.as_view()),
## 签发token方法二:
url(r'^jwtlogin2/$', obtain_jwt_token),
]


JWT认证校验token
视图 |
---|
from rest_framework_jwt.views import APIView
from utils.response import APIResponse
##必须 登录后 才能访问,通过了认证权限组件
from rest_framework.permissions import IsAuthenticated
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
class UserDetail(APIView):
authentication_classes = [JSONWebTokenAuthentication]
permission_classes = [IsAuthenticated]
def get(self, request, *args, **kwargs):
return APIResponse(results={'username': request.user.username})
路由 |
---|
from django.conf.urls import url
from rest_framework_jwt.views import ObtainJSONWebToken, obtain_jwt_token
from . import views
urlpatterns = [
## 签发token方法一 :
url(r'^jwtlogin1/$', ObtainJSONWebToken.as_view()),
## 签发token方法二:
url(r'^jwtlogin2/$', obtain_jwt_token),
url(r'^user/detail/$', views.UserDetail.as_view()),
]

先登录,拿到token信息
代码语言:javascript复制{
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNTkzMzQxODM2LCJlbWFpbCI6IiJ9.ZkLlvwwGxZNW6yQCB3SLMiv3LNgk4-Jkajp7Nc5PODQ"
}

通过get方法,提供token登录

还是不行,为啥呢?因为jwt做了反爬,需要在token前面加个'jwt ' 注意:有空格

签发token源码分析
入口 |
---|
# 前提:给一个局部禁用了所有 认证与权限 的视图类发送用户信息得到token,其实就是登录接口
# 1)rest_framework_jwt.views.ObtainJSONWebToken 的 父类 JSONWebTokenAPIView 的 post 方法
# 接受有username、password的post请求
# 2)post方法将请求数据交给 rest_framework_jwt.serializer.JSONWebTokenSerializer 处理
# 完成数据的校验,会走序列化类的 全局钩子校验规则,校验得到登录用户并签发token存储在序列化对象中
核心源码:rest_framework_jwt.serializer.JSONWebTokenSerializer的validate(self, attrs)方法
代码语言:javascript复制def validate(self, attrs):
# 账号密码字典
credentials = {
self.username_field: attrs.get(self.username_field),
'password': attrs.get('password')
}
if all(credentials.values()):
# 签发token第1步:用账号密码得到user对象
user = authenticate(**credentials)
if user:
if not user.is_active:
msg = _('User account is disabled.')
raise serializers.ValidationError(msg)
# 签发token第2步:通过user得到payload,payload包含着用户信息与过期时间
payload = jwt_payload_handler(user)
# 在视图类中,可以通过 序列化对象.object.get('user'或者'token') 拿到user和token
return {
# 签发token第3步:通过payload签发出token
'token': jwt_encode_handler(payload),
'user': user
}
else:
msg = _('Unable to log in with provided credentials.')
raise serializers.ValidationError(msg)
else:
msg = _('Must include "{username_field}" and "password".')
msg = msg.format(username_field=self.username_field)
raise serializers.ValidationError(msg)
手动签发token |
---|
# 1)通过username、password得到user对象
# 2)通过user对象生成payload:jwt_payload_handler(user) => payload
# from rest_framework_jwt.serializers import jwt_payload_handler
# 3)通过payload签发token:jwt_encode_handler(payload) => token
# from rest_framework_jwt.serializers import jwt_encode_handler
校验token源码分析
源码入口 |
---|
# 前提:访问一个配置了jwt认证规则的视图类,就需要提交认证字符串token,在认证类中完成token的校验
# 1)rest_framework_jwt.authentication.JSONWebTokenAuthentication 的 父类 BaseJSONWebTokenAuthentication 的 authenticate 方法
# 请求头拿认证信息jwt-token => 通过反爬小规则确定有用的token => payload => user
核心源码:rest_framework_jwt.authentication.BaseJSONWebTokenAuthentication的authenticate(self, request)方法
代码语言:javascript复制def authenticate(self, request):
"""
Returns a two-tuple of `User` and token if a valid signature has been
supplied using JWT-based authentication. Otherwise returns `None`.
"""
# 带有反爬小规则的获取token:前台必须按 "jwt token字符串" 方式提交
# 校验user第1步:从请求头 HTTP_AUTHORIZATION 中拿token,并提取
jwt_value = self.get_jwt_value(request)
# 游客
if jwt_value is None:
return None
# 校验
try:
# 校验user第2步:token => payload
payload = jwt_decode_handler(jwt_value)
except jwt.ExpiredSignature:
msg = _('Signature has expired.')
raise exceptions.AuthenticationFailed(msg)
except jwt.DecodeError:
msg = _('Error decoding signature.')
raise exceptions.AuthenticationFailed(msg)
except jwt.InvalidTokenError:
raise exceptions.AuthenticationFailed()
# 校验user第3步:token => payload
user = self.authenticate_credentials(payload)
return (user, jwt_value)
手动校验token |
---|
# 1)从请求头中获取token
# 2)根据token解析出payload:jwt_decode_handler(token) => payloay
# from rest_framework_jwt.authentication import jwt_decode_handler
# 3)根据payload解析出user:self.authenticate_credentials(payload) => user
# 继承drf-jwt的BaseJSONWebTokenAuthentication,拿到父级的authenticate_credentials方法
自定义jwt的配置
settings文件配置 |
---|
# 自定义 drf-jwt 配置
import datetime
JWT_AUTH = {
# user => payload
'JWT_PAYLOAD_HANDLER':
'rest_framework_jwt.utils.jwt_payload_handler',
# payload => token
'JWT_ENCODE_HANDLER':
'rest_framework_jwt.utils.jwt_encode_handler',
# token => payload
'JWT_DECODE_HANDLER':
'rest_framework_jwt.utils.jwt_decode_handler',
# token过期时间
'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7),
# token刷新的过期时间
'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(days=7),
# 反爬小措施前缀
'JWT_AUTH_HEADER_PREFIX': 'JWT',
}
多方式登录签发token
手机号登录 邮箱登录 用户密码登录
路由 |
---|
from django.conf.urls import url
from rest_framework_jwt.views import ObtainJSONWebToken, obtain_jwt_token
from . import views
urlpatterns = [
## 签发token方法一 :
url(r'^jwtlogin1/$', ObtainJSONWebToken.as_view()),
## 签发token方法二:
url(r'^jwtlogin2/$', obtain_jwt_token),
url(r'^user/detail/$', views.UserDetail.as_view()),
url(r'^login/$', views.LoginAPIView.as_view()),
]
修改APIResponse |
---|
from rest_framework.response import Response
class APIResponse(Response):
def __init__(self, data_status=0, data_msg='ok', results=None, http_status=None, headers=None, exception=False, **kwargs):
# data的初始状态:状态码与状态信息
data = {
'status': data_status,
'msg': data_msg,
}
# data的响应数据体
# results可能是False、0等数据,这些数据某些情况下也会作为合法数据返回
if results is not None:
data['results'] = results
# data响应的其他内容
# if kwargs is not None:
# for k, v in kwargs.items():
# setattr(data, k, v)
data.update(kwargs)
super().__init__(data=data, status=http_status, headers=headers, exception=exception)
"""
Response({
'status': 0,
'msg': 'ok',
'results': [],
'token': '' # 有这样的额外的key-value数据结果
},status=http_status,headers=headers,exception=True|False)
APIResponse() => Response({'status': 0,'msg': 'ok'})
"""
封装一个序列化类 |
---|
from rest_framework import serializers
from . import models
import re
from rest_framework_jwt.settings import api_settings
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
class UserModelSerializer(serializers.ModelSerializer):
## 自定义反序列化字段
usr = serializers.CharField(write_only=True)
pwd = serializers.CharField(write_only=True)
class Meta:
model = models.User
fields = ['usr', 'pwd']
extra_kwargs = {
# 系统校验规则
}
def validate_usr(self, value):
return value
def validate_pwd(self, value):
return value
def validate(self, attrs):
usr = attrs.get('usr')
pwd = attrs.get('pwd')
if re.match(r'. @. ', usr, re.I):
user_query = models.User.objects.filter(email=usr)
elif re.match(r'1[3-9][0-9]{9}', usr):
user_query = models.User.objects.filter(mobile=usr)
else:
user_query = models.User.objects.filter(username=usr)
user_obj = user_query.first()
if user_obj and user_obj.check_password(pwd):
## 签发token,将token存放到序列化对象的token名字中
payload = jwt_payload_handler(user_obj)
self.token = jwt_encode_handler(payload)
return attrs
raise serializers.ValidationError({'data': '数据有误'})
视图 |
---|
from rest_framework_jwt.views import APIView
from utils.response import APIResponse
from rest_framework.permissions import IsAuthenticated
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
"""
#多方式登录签发token
1.手机号登录
2.邮箱登录
3.用户密码登录
"""
from . import serializers
from utils.response import APIResponse
class LoginAPIView(APIView):
## 1.禁用认证与权限组件
authentication_classes = []
permission_classes = []
## 2.拿到前台登录信息
def post(self, request, *args, **kwargs):
## 3.校验得到登录用户
# 前台账号用usr传,密码用pwd传
user_ser = serializers.UserModelSerializer(data=request.data)
user_ser.is_valid(raise_exception=True)
## 4.签发token并返回
return APIResponse(token=user_ser.token)

返回登录用户信息 |
---|
serializers.py
代码语言:javascript复制from rest_framework import serializers
from . import models
import re
from rest_framework_jwt.settings import api_settings
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
class UserModelSerializer(serializers.ModelSerializer):
## 自定义反序列化字段
usr = serializers.CharField(write_only=True)
pwd = serializers.CharField(write_only=True)
class Meta:
model = models.User
fields = ['usr', 'pwd', 'username', 'mobile', 'email']
extra_kwargs = {
# 系统校验规则
'username': {
'read_only': True
},
'mobile': {
'read_only': True
},
'email': {
'read_only': True
},
}
def validate_usr(self, value):
return value
def validate_pwd(self, value):
return value
def validate(self, attrs):
usr = attrs.get('usr')
pwd = attrs.get('pwd')
if re.match(r'. @. ', usr, re.I):
user_query = models.User.objects.filter(email=usr)
elif re.match(r'1[3-9][0-9]{9}', usr):
user_query = models.User.objects.filter(mobile=usr)
else:
user_query = models.User.objects.filter(username=usr)
user_obj = user_query.first()
if user_obj and user_obj.check_password(pwd):
## 签发token,将token存放到序列化对象的token名字中
payload = jwt_payload_handler(user_obj)
token = jwt_encode_handler(payload)
## 将当前用户和签发的token都保存在序列化对象中
self.user = user_obj
self.token = token
return attrs
raise serializers.ValidationError({'data': '数据有误'})
views.py
代码语言:javascript复制from rest_framework_jwt.views import APIView
from utils.response import APIResponse
from rest_framework.permissions import IsAuthenticated
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
"""
#多方式登录签发token
1.手机号登录
2.邮箱登录
3.用户密码登录
"""
from . import serializers
from utils.response import APIResponse
class LoginAPIView(APIView):
## 1.禁用认证与权限组件
authentication_classes = []
permission_classes = []
## 2.拿到前台登录信息
def post(self, request, *args, **kwargs):
## 3.校验得到登录用户
# 前台账号用usr传,密码用pwd传
user_ser = serializers.UserModelSerializer(data=request.data)
user_ser.is_valid(raise_exception=True)
## 4.签发token并返回
return APIResponse(token=user_ser.token, results=serializers.UserModelSerializer(user_ser.user).data)
用户名密码登录

邮箱登录

因为没有手机号,所以无法使用手机登录

群查数据准备
模型层 |
---|
from django.db import models
## 用户表:角色groups 权限user_permissions
## 角色表:用户user_set 权限permissions
## 权限表:用户user_set 角色group_set
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
mobile = models.CharField(max_length=11, unique=True)
class Meta:
db_table = 'api_user'
verbose_name = '用户表'
verbose_name_plural = verbose_name
def __str__(self):
return self.username
class Car(models.Model):
name = models.CharField(max_length=16, unique=True, verbose_name='车名')
price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='价格')
brand = models.CharField(max_length=16, verbose_name='品牌')
class Meta:
db_table = 'api_car'
verbose_name = '汽车表'
verbose_name_plural = verbose_name
def __str__(self):
return self.name
注册admin |
---|
from django.contrib import admin
from . import models
# Register your models here.
from django.contrib.auth.admin import UserAdmin
admin.site.register(models.User,UserAdmin)
admin.site.register(models.Car)
数据库迁移 |
---|
MacBook-pro:dg_proj_n driverzeng$ python3 manage.py makemigrations
MacBook-pro:dg_proj_n driverzeng$ python3 manage.py migrate
 ... 加了一万条,这里就不一一截图了。

群查过滤组件前戏
路由层 |
---|
from django.conf.urls import url
from rest_framework_jwt.views import ObtainJSONWebToken, obtain_jwt_token
from . import views
urlpatterns = [
## 签发token方法一 :
url(r'^jwtlogin1/$', ObtainJSONWebToken.as_view()),
## 签发token方法二:
url(r'^jwtlogin2/$', obtain_jwt_token),
url(r'^user/detail/$', views.UserDetail.as_view()),
url(r'^login/$', views.LoginAPIView.as_view()),
url(r'^cars/$', views.CarListAPIView.as_view()),
]
视图层 |
---|
## Car群查接口
from rest_framework.generics import ListAPIView
from . import models
class CarListAPIView(ListAPIView):
queryset = models.Car.objects
serializer_class = serializers.CarModelSerializer
序列化 |
---|
class CarModelSerializer(serializers.ModelSerializer):
class Meta:
model = models.Car
fields = ['name', 'price', 'brand']

群查之搜索过滤组件
视图层 |
---|
## Car群查接口
from rest_framework.generics import ListAPIView
from . import models
# 1)drf的SearchFilter
from rest_framework.filters import SearchFilter
class CarListAPIView(ListAPIView):
queryset = models.Car.objects
serializer_class = serializers.CarModelSerializer
## 局部配置过滤类们(全局配置DEFAULT_FILTER_BACKENDS)
filter_backends = [SearchFilter]
## SearchFilter过滤类,依赖的过滤条件 => 接口:/cars/?search=...
search_fields = ['name']

/cars/?search=1
,指定的字段中,如果都包含1,都会被查出来,如果search_fields = ['name','price']
有两个字段的时候,那么价格里带1的都会出来。
## Car群查接口
from rest_framework.generics import ListAPIView
from . import models
# 1)drf的SearchFilter
from rest_framework.filters import SearchFilter
class CarListAPIView(ListAPIView):
queryset = models.Car.objects
serializer_class = serializers.CarModelSerializer
## 局部配置过滤类们(全局配置DEFAULT_FILTER_BACKENDS)
filter_backends = [SearchFilter]
## SearchFilter过滤类,依赖的过滤条件 => 接口:/cars/?search=...
search_fields = ['name', 'price']

群查之排序过滤组件
视图层 |
---|
## Car群查接口
from rest_framework.generics import ListAPIView
from . import models
# 1)drf的SearchFilter
from rest_framework.filters import SearchFilter
# 2)drf的OrderingFilter
from rest_framework.filters import OrderingFilter
class CarListAPIView(ListAPIView):
queryset = models.Car.objects
serializer_class = serializers.CarModelSerializer
## 局部配置过滤类们(全局配置DEFAULT_FILTER_BACKENDS)
filter_backends = [SearchFilter,OrderingFilter]
## SearchFilter过滤类,依赖的过滤条件 => 接口:/cars/?search=...
search_fields = ['name', 'price']
## ordering 排序类,依赖排序条件 => 接口:/cars/?ordering=
ordering_fields = ['price']


多个条件,就在ordering_fields = ['price','pk']
中添加,url:http://127.0.0.1:8000/api/cars/?search=1&ordering=-price,pk
基础分页组件
分页组件 |
---|
在api目录下创建一个pagenations.py文件
代码语言:javascript复制from rest_framework.pagination import PageNumberPagination
class MyPageNumberPagination(PageNumberPagination):
## 默认一页显示3条
page_size = 3
## url中传递参数的名字,默认是配置,自己可以修改
page_query_param = 'page'
## 用户可以自定义显示多少条
page_size_query_param = 'page_size'
## 用户如果自定义,但是最大只能显示5条
max_page_size = 5
视图层 |
---|
一个视图类只能配一个分页器。
代码语言:javascript复制## Car群查接口
from rest_framework.generics import ListAPIView
from . import models
# 1)drf的SearchFilter
from rest_framework.filters import SearchFilter
# 2)drf的OrderingFilter
from rest_framework.filters import OrderingFilter
# 3)drf的分页类 - 自定义
from . import pagenations
class CarListAPIView(ListAPIView):
queryset = models.Car.objects.all()
serializer_class = serializers.CarModelSerializer
## 局部配置过滤类们(全局配置DEFAULT_FILTER_BACKENDS)
filter_backends = [SearchFilter, OrderingFilter]
## SearchFilter过滤类,依赖的过滤条件 => 接口:/cars/?search=...
search_fields = ['name', 'price']
## ordering 排序类,依赖排序条件 => 接口:/cars/?ordering=
ordering_fields = ['price']
## 分页组件 - 给视图类配置分页类即可
pagination_class = pagenations.MyPageNumberPagination

偏移分页器
分页组件 |
---|
pagenations.py
代码语言:javascript复制from rest_framework.pagination import LimitOffsetPagination
class MyLimitOffsetPagination(LimitOffsetPagination):
## 默认显示3条数据
default_limit = 3
## 查询关键字
limit_query_param = 'limit'
## 偏移关键字
offset_query_param = 'offset'
## 最大显示5条
max_limit = 5
视图层 |
---|
## Car群查接口
from rest_framework.generics import ListAPIView
from . import models
# 1)drf的SearchFilter
from rest_framework.filters import SearchFilter
# 2)drf的OrderingFilter
from rest_framework.filters import OrderingFilter
# 3)drf的分页类 - 自定义
from . import pagenations
class CarListAPIView(ListAPIView):
queryset = models.Car.objects.all()
serializer_class = serializers.CarModelSerializer
## 局部配置过滤类们(全局配置DEFAULT_FILTER_BACKENDS)
filter_backends = [SearchFilter, OrderingFilter]
## SearchFilter过滤类,依赖的过滤条件 => 接口:/cars/?search=...
search_fields = ['name', 'price']
## ordering 排序类,依赖排序条件 => 接口:/cars/?ordering=
ordering_fields = ['price']
## 分页组件 - 给视图类配置分页类即可
# pagination_class = pagenations.MyPageNumberPagination
pagination_class = pagenations.MyLimitOffsetPagination


取出价格最高的前4辆车

自定义过滤器
过滤组件 |
---|
在api目录下创建filters.py
代码语言:javascript复制class LimitFilter:
def filter_queryset(self, request, queryset, view):
limit = request.query_params.get('limit')
print(limit)
return queryset
视图层 |
---|
## Car群查接口
from rest_framework.generics import ListAPIView
from . import models
# 1)drf的SearchFilter
from rest_framework.filters import SearchFilter
# 2)drf的OrderingFilter
from rest_framework.filters import OrderingFilter
# 3)drf的分页类 - 自定义
from . import pagenations
## 自定义过滤器
from api.filters import LimitFilter
class CarListAPIView(ListAPIView):
queryset = models.Car.objects.all()
serializer_class = serializers.CarModelSerializer
## 局部配置过滤类们(全局配置DEFAULT_FILTER_BACKENDS)
filter_backends = [SearchFilter, OrderingFilter, LimitFilter]
## SearchFilter过滤类,依赖的过滤条件 => 接口:/cars/?search=...
search_fields = ['name', 'price']
## ordering 排序类,依赖排序条件 => 接口:/cars/?ordering=
ordering_fields = ['price']
## 分页组件 - 给视图类配置分页类即可
# pagination_class = pagenations.MyPageNumberPagination
pagination_class = pagenations.MyLimitOffsetPagination

修改自定义过滤器
代码语言:javascript复制class LimitFilter:
def filter_queryset(self, request, queryset, view):
limit = request.query_params.get('limit')
if limit:
limit = int(limit)
return queryset[:limit]
return queryset

游标分页器(了解)
分页组件 |
---|
from rest_framework.pagination import CursorPagination
class MyCursorPagination(CursorPagination):
## 关键字
cursor_query_param = 'cursor'
## 默认页码数
page_size = 3
## 用户可以自定义的页数的关键字
page_size_query_param = 'page_size'
## 最大页码数
max_page_size = 10
## 排序规则,默认按照主键,前提条件,视图层得把OrderingFilter注释掉,否则会报错
ordering = '-pk'
视图层 |
---|
## Car群查接口
from rest_framework.generics import ListAPIView
from . import models
# 1)drf的SearchFilter
from rest_framework.filters import SearchFilter
# 2)drf的OrderingFilter
from rest_framework.filters import OrderingFilter
# 3)drf的分页类 - 自定义
from . import pagenations
## 自定义过滤器
from api.filters import LimitFilter
class CarListAPIView(ListAPIView):
queryset = models.Car.objects.all()
serializer_class = serializers.CarModelSerializer
## 局部配置过滤类们(全局配置DEFAULT_FILTER_BACKENDS)
filter_backends = [SearchFilter, OrderingFilter, LimitFilter]
## SearchFilter过滤类,依赖的过滤条件 => 接口:/cars/?search=...
search_fields = ['name', 'price']
## ordering 排序类,依赖排序条件 => 接口:/cars/?ordering=
ordering_fields = ['price']
## 分页组件 - 给视图类配置分页类即可
# pagination_class = pagenations.MyPageNumberPagination
## 分页组件 - 偏移分页器
# pagination_class = pagenations.MyLimitOffsetPagination
## 分页组件 - 游标分页器
pagination_class = pagenations.MyCursorPagination

Django过滤器插件
主要来做分类查询,上面所写的都是模糊查询,没有精确到分类。
django-filter安装 |
---|
MacBook-pro:~ driverzeng$ pip3 install django-filter -i https://mirrors.aliyun.com/pypi/simple/
视图层 |
---|
## Car群查接口
from rest_framework.generics import ListAPIView
from . import models
# 1)drf的SearchFilter
from rest_framework.filters import SearchFilter
# 2)drf的OrderingFilter
from rest_framework.filters import OrderingFilter
# 3)drf的分页类 - 自定义
from . import pagenations
## 自定义过滤器
from api.filters import LimitFilter
## django-filter过滤器插件
from django_filters.rest_framework import DjangoFilterBackend
class CarListAPIView(ListAPIView):
queryset = models.Car.objects.all()
serializer_class = serializers.CarModelSerializer
## 局部配置过滤类们(全局配置DEFAULT_FILTER_BACKENDS)
filter_backends = [SearchFilter, OrderingFilter, LimitFilter, DjangoFilterBackend]
## SearchFilter过滤类,依赖的过滤条件 => 接口:/cars/?search=...
search_fields = ['name', 'price']
## ordering 排序类,依赖排序条件 => 接口:/cars/?ordering=
ordering_fields = ['price']
如何使用django-filter?
先在api目录下的filter.py文件中中自定义一个类
代码语言:javascript复制## django-filter插件过滤器类
from django_filters.rest_framework import FilterSet
from . import models
class CarFilterSet(FilterSet):
class Meta:
model = models.Car
fields = ['brand']
然后在视图中导入类,并且在filter_class中配置
代码语言:javascript复制## Car群查接口
from rest_framework.generics import ListAPIView
from . import models
# 1)drf的SearchFilter
from rest_framework.filters import SearchFilter
# 2)drf的OrderingFilter
from rest_framework.filters import OrderingFilter
# 3)drf的分页类 - 自定义
from . import pagenations
## 自定义过滤器
from api.filters import LimitFilter
## django-filter过滤器插件
from django_filters.rest_framework import DjangoFilterBackend
## 导入自定义的类
from api.filters import CarFilterSet
class CarListAPIView(ListAPIView):
queryset = models.Car.objects.all()
serializer_class = serializers.CarModelSerializer
## 局部配置过滤类们(全局配置DEFAULT_FILTER_BACKENDS)
filter_backends = [SearchFilter, OrderingFilter, LimitFilter, DjangoFilterBackend]
## SearchFilter过滤类,依赖的过滤条件 => 接口:/cars/?search=...
search_fields = ['name', 'price']
## ordering 排序类,依赖排序条件 => 接口:/cars/?ordering=
ordering_fields = ['price']
## 分页组件 - 给视图类配置分页类即可
# pagination_class = pagenations.MyPageNumberPagination
## 分页组件 - 偏移分页器
# pagination_class = pagenations.MyLimitOffsetPagination
## 分页组件 - 游标分页器
# pagination_class = pagenations.MyCursorPagination
## django-filter过滤器插件使用,一下两个配置都可以,一般使用filter_class
filter_class = CarFilterSet
# filter_fields = []

django-filter重点使用方法
自定义过滤字段 |
---|
api目录下的filter.py文件中
代码语言:javascript复制## django-filter插件过滤器类
from django_filters.rest_framework import FilterSet
from . import models
from django_filters import filters
class CarFilterSet(FilterSet):
min_price = filters.NumberFilter(field_name='price', lookup_expr='gte')
class Meta:
model = models.Car
fields = ['brand', 'min_price']

再来个最高价格
代码语言:javascript复制## django-filter插件过滤器类
from django_filters.rest_framework import FilterSet
from . import models
from django_filters import filters
class CarFilterSet(FilterSet):
min_price = filters.NumberFilter(field_name='price', lookup_expr='gte')
max_price = filters.NumberFilter(field_name='price', lookup_expr='lte')
class Meta:
model = models.Car
fields = ['brand', 'min_price', 'max_price']

drf大总结
接口
代码语言:javascript复制"""
1、什么是接口:url 请求参数 响应数据 | 接口文档
2、接口规范:
url:https,api,资源(名词复数),v1,get|post表示操作资源的方式,?过滤条件
响应数据:status,msg,results|data,url资源数据
"""
DRF入门
代码语言:javascript复制"""
1、封装风格
2、请求生命周期:走APIView的as_view => View的as_view调用APIView的dispatch => 分发给视图类的请求函数 => 响应
3、请求模块:二次封装request
4、解析模块:局部全局配置,form-data、urlencoding、json
5、渲染模块:局部全局配置,html、json
6、响应模块:Response(data, http_status, header)
data={'detail': serializer_obj.errors} => http_status=4..|5..
# 4,5开头字段状态码,前台axios请求响应会解析到catch分支
axios({}).then().catch()
7、二次封装Response:
i) 继承Response
ii) 自定义参数格式化成data,传给 super().__init__(data=data)
iii) 网络状态码、响应头等信息带给super().__init__()
8、异常模块:在请求生命周期中,分发请求任务执行时,被一个超大的try except包裹,一旦出现任何异常,都会交给exception_handler函数处理
自定义原因:系统只处理了部分,所以手动还需要处理;后期异常信息需要记录到日志文件
工作流程:先交给drf处理异常,如果有响应结果,代表drf已处理,如果是None,自己处理
"""
序列化组件
代码语言:javascript复制# 1)可以将model对象序列化成可以返回给前台的json数据
# 2)可以校验前台传来的数据,完成model的增与改
模型层
代码语言:javascript复制class User(models.Model):
name = models.CharField(max_length=16)
# 自定义插拔序列化字段 - 完成连表深度查询
@property
def my_name(self):
return self.name
序列化层
代码语言:javascript复制class UserListSerializer(serializers.ListSerializer):
def update(self, instance, validated_data):
for index, obj in enumerate(instance):
self.child.update(obj, validated_data[index])
return instance
class UserModelSerializer(serializers.ModelSerializer):
# 自定义反序列化不入库字段 - 要在钩子函数校验后移除
check_name = serializers.CharFiled(write_only=True)
class Meta:
model = User
fields = ['name', 'my_name', 'check_name']
extra_kwargs = {
'my_name': {
'read_only': True
}
}
# 群改
list_serializer_class = UserListSerializer
# 局部、全局钩子
# create、update方法不需要重写,可以完成单增、群增、单改
视图层
代码语言:javascript复制class UserAPIView(APIView):
def get(self, request, *args, **kwargs):
# 先orm得到 user_query | user_obj
# 在得到序列化对象
user_ser = UserModelSerializer(user_query, many=True)
user_ser = UserModelSerializer(user_query, many=False)
# 返回序列化结果 user_ser.data
def post(self, request, *args, **kwargs):
# 数据给序列化类
user_ser = UserModelSerializer(data=user_list, many=True)
user_ser = UserModelSerializer(data=user_dic, many=False)
# 校验数据
user_ser.is_valid(raise_exception=True)
# 数据库操作
user_query|user_obj = user_ser.save()
def patch(self, request, *args, **kwargs):
# 根据主键或主键们确定要修改的 对象 或 对象们
# 数据给序列化类
user_ser = UserModelSerializer(data=user_list, many=True, instance=对象们, partial=True|False)
user_ser = UserModelSerializer(data=user_dic, many=False, instance=对象, partial=True|False)
# 校验数据
user_ser.is_valid(raise_exception=True)
# 数据库操作
user_query|user_obj = user_ser.save()
三大认证
代码语言:javascript复制"""
1、认证组件:
i)自定义认证类继承BaseAuthentication,重写authenticate方法
ii)规则:游客返回None,非法用户抛异常,合法用户返回(user, auth)
iii)全局局部配置
2、权限组件:
i)自定义权限类继承BasePermission,重写has_permission方法
ii)规则:有权限返回True,无权限返回False - 校验对象:request.user或请求方式
iii)全局局部配置
3、评论组件:
i)自定义频率类继承SimpleRateThrottle,重写get_cache_key方法,设置scope类属性
ii)全局配置scope对应的访问频率:次数/时间
iii)get_cache_key返回的是与限制条件有关的动态字符串 - 限制条件来源于请求参数或请求头
iv)全局局部配置
4、jwt认证:
i)客户端存token,服务器存签发算法、校验算法、秘钥
ii)基本信息(头base64).用户信息过期时间(载荷base64).全信息 秘钥(签名HS256)
iii)drf-jwt组件使用
iv)登录接口签发token
v)认证组件校验token
"""
群查过滤组件
代码语言:javascript复制"""
1、搜索组件:search_fields = ['name', 'price']
2、排序组件:ordering_fields = ['pk', 'price']
3、分页组件:pagination_class = pagenations.MyPageNumberPagination
4、django-filter过滤插件:filter_class = CarFilterSet
5、自定义过滤器:自定义过滤类实现filter_queryset即可,根据筛选条件筛选后再返回queryset即可
使用:
i)视图类添加过滤类:filter_backends = [SearchFilter, OrderingFilter, LimitFilter, DjangoFilterBackend]
ii)为不同的过滤类配置过滤条件
"""