目录
- DRF规范
- 请求、响应
- 路由
- 视图
- 序列化
- 认证、权限、频率
- 过滤器、排序、分页、异常处理
- JWT
- RBAC
DRF规范
REST是Representational State Transfer的简称,中文翻译为“表征状态转移”或“表现层状态转化”。
十条RESTful API设计指南
1、数据的安全保障
url一般采用https协议进行传输,可以提高数据交互过程中安全性
2、接口特征表现
使用api关键字标识接口url,代表该url是完成前后台数据交互的
3、多数据版本共存
当一种数据资源有多个版本,应在url中标识数据版本
4、数据即资源
均使用名词复数表示数据资源
5、请求方式决定资源的操作方式
GET :从服务器取出全部资源
GET:后面加数字,从服务器取出单个资源
POST:在服务器新建一个资源
PUT:在服务器更新资源(客户端提供改变后的完整资源)
PATCH:在服务器更新资源(客户端提供改变的属性)
DELETE :从服务器删除资源
6、响应状态码
200:常规请求
201:创建成功
300:永久重定向
301:暂时重定向
403:请求无权限
404:请求路径不存在
405:请求方法不存在
500:服务器异常
7、响应结果
针对不同操作,服务器向用户返回不同的结果
GET:返回资源对象的列表(数组)
GET:后面加数字,返回单个资源对象
POST:返回新生成的资源对象
PUT:返回完整的资源对象
PATCH:返回完整的资源对象
DELETE:返回一个空文档
8、过滤
通过url上传参的形式传递搜索条件
https://api.example.com/v1/zoos?limit=10:指定返回记录的数量
https://api.example.com/v1/zoos?offset=10:指定返回记录的开始位置
https://api.example.com/v1/zoos?page=2&per_page=100:指定第几页,以及每页的记录数
https://api.example.com/v1/zoos?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序
https://api.example.com/v1/zoos?animal_type_id=1:指定筛选条件
9、错误处理
请求出错时,应返会错误信息
10、Hypermedia API
返回结果中提供链接
请求、响应
请求
Request
基于Django的request进行重新封装,Django的request的属性都可以使用。
对于POST请求、PUT请求的数据通过request.data获取
对于GET请求的参数通过request.query_params获取
解析器
根据请求头content-type选择对应的解析器对请求体内容进行处理
JSONParser:仅处理content-type为application/json的请求头
FormParser:仅处理请求头content-type为application/x-www-form-urlencoded 的请求体
MultiPartParser:仅处理请求头content-type为multipart/form-data的请求体
FileUploadParser:仅处理上传的文件
局部使用
重写类的parser_classes属性
from rest_framework.views import APIView
from rest_framework.parsers import JSONParser, MultiPartParser, FileUploadParser, FormParser
class BookView(APIView):
parser_classes = [JSONParser, MultiPartParser] # 局部使用,重写类的parser_classes属性
def get(self, request):
pass
def post(self, request):
pass
全局使用
配置settings.py
REST_FRAMEWORK = {
'DEFAULT_PARSER_CLASSES':[
'rest_framework.parsers.JSONParser',
'rest_framework.parsers.FormParser',
'rest_framework.parsers.MultiPartParser'
]
}
响应
Response
自己封装Response
1.继承Response
2.重写__init__方法
3.调用父类的__init__方法
from rest_framework.response import Response
class ApiResponse(Response):
def __init__(self, code=100, msg='', err='', data=None, status=None, headers=None, **kwargs):
dic = {'code': code}
if msg:
dic['msg'] = msg
if err:
dic['err'] = err
if data:
dic['data'] = data
dic.update(kwargs)
super().__init__(data=dic, status=status, headers=headers)
渲染器
内置渲染器
默认显示格式:BrowsableAPIRenderer
访问URL方式:
http://127.0.0.1:8000/test/?format=api
http://127.0.0.1:8000/test.api
http://127.0.0.1:8000/test/
显示json格式:JSONRenderer
访问URL方式:
http://127.0.0.1:8000/test/?format=json
http://127.0.0.1:8000/test.json
http://127.0.0.1:8000/test/
显示表格格式:AdminRenderer
访问URL方式:
http://127.0.0.1:8000/test/?format=admin
http://127.0.0.1:8000/test.admin
http://127.0.0.1:8000/test/
显示表单格式:HTMLFormRenderer
访问URL方式:
http://127.0.0.1:8000/test/?format=form
http://127.0.0.1:8000/test.form
http://127.0.0.1:8000/test/
自定义显示模板格式:TemplateHTMLRenderer
局部使用
重写视图类的renderer_classes属性
from rest_framework.views import APIView
from rest_framework.renderers import HTMLFormRenderer, BrowsableAPIRenderer
class BookView(APIView):
renderer_classes = [HTMLFormRenderer, BrowsableAPIRenderer] # 局部使用,重写类的renderer_classes属性
def get(self, request):
pass
def post(self, request):
pass
全局使用
配置settings.py
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES':['rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.BrowsableAPIRenderer']
}
路由
自定义路由(原始方法)
from django.urls import path, re_path
from app01 import views
urlpatterns = [
path('test/', views.TestView.as_view()),
re_path(r'^test/(?P<pk>\d+)$', views.TestView.as_view())
]
半自动路由(视图类继承ViewSetMixin)
传入一个字典{'请求方式':'视图对应的函数名'}
from django.urls import path, re_path
from app01 import views
urlpatterns = [
path('test/', views.TestView.as_view({'get': 'list', 'post': 'create'})), # 传入一个字典{'请求方式':'视图对应的函数名'}
re_path(r'test/(?P<pk>\d+)$', views.TestView.as_view({'get': 'retrieve', 'put': 'update', 'delete': 'destroy'}))
]
全自动路由(自动生成)
1.导入自动生成路由的类
2.实例化
3.注册视图函数
4.拼接路由
# urls
from rest_framework.routers import SimpleRouter, DefaultRouter
from app01 import views
route1 = SimpleRouter() # 实例化
route2 = DefaultRouter()
route1.register('app01/test1', views.TestView) # 注册视图函数,第一个参数为路由地址,第二给参数为视图类
route2.register('app01/test2', views.Test2View)
urlpatterns += route1.urls # 拼接路由,urls为自动生成的路由列表
urlpatterns += route2.urls
action装饰器:函数使用action装饰器装饰后,会自动生成路由
# views.py
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet
from app01 import models
from utils import ser
class TestView(ModelViewSet):
queryset = models.AgvTestRecord.objects.all()
serializer_class = ser.TestSer
@action(methods=['get'], detail=True, url_path='ccc')
"""
methods:请求方式列表
detail:决定是否带有pk值
当为True时生成的路由:^app01/test1/(?P<pk>[^/.]+)/ccc/$ [name='agvtestrecord-action']
当为False时生成的路由:^app01/test1/ccc/$ [name='agvtestrecord-action']
url_path:生成的url路径名,默认为被装饰函数名
url_name:反向解析名,默认为被装饰函数名
"""
def action(self, request, *args, **kwargs):
return Response('OK')
视图
两个视图基类
ApiView:基类,继承View
GenericView:基类,继承ApiView
ApiView
from rest_framework.views import ApiView
from utils.utils import ApiResponse
from app01 import models
from utils import sers
常用类属性:
parser_classes:元组或列表,配置解析器类
renderer_classes:元组或列表,配置渲染器类
authentication_classes:元组或列表,配置认证类
permission_classes:元组或列表,配置权限类
throttle_classes:元组或列表,配置频率类
class TestView(APIView):
def get(self, request, *args, **kwargs):
pk = kwargs.get('pk', None)
if pk:
obj = models.AgvTestRecord.objects.filter(pk=pk).first()
ser_obj = sers.TestSer(obj)
return ApiResponse(code=100, msg='success', data=ser_obj.data)
else:
obj = models.AgvTestRecord.objects.all()
ser_obj = sers.TestSer(obj, many=True)
return ApiResponse(code=100, msg='success', data=ser_obj.data)
def post(self, request, *args, **kwargs):
obj = sers.TestSer(data=request.data)
obj.is_valid(raise_exception=True)
obj.save()
return ApiResponse(code=101, msg='success', data=obj.data)
GenericApiView
from rest_framework.generics import GenericAPIView
from utils.utils import ApiResponse
from app01 import models
from utils import sers
常用类属性:
queryset:模型类对象的全部
serializer_class:序列化类
常用方法:
self.get_queryset():获得全部的queryset对象
self.get_object():获得单个queryset对象
self.get_serializer(*args, **kwagrs):获得序列化类,并且序列化及反序列化
class TestView(GenericAPIView):
queryset = models.AgvTestRecord.objects.all()
serializer_class = sers.TestSer
def get(self, request, *args, **kwargs):
pk = kwargs.get('pk', None)
if pk:
obj = self.get_object()
ser_obj = self.get_serializer(obj)
return ApiResponse(code=100, msg='success', data=ser_obj.data)
else:
obj = self.get_queryset()
ser_obj = self.get_serializer(obj, many=True)
return ApiResponse(code=100, msg='success', data=ser_obj.data)
def post(self, request, *args, **kwargs):
obj = self.get_serializer(data=request.data)
obj.is_valid(raise_exception=True)
obj.save()
return ApiResponse(code=101, msg='success', data=obj.data)
五个视图拓展类
一般与GenericApiView一起使用
CreateModelMixin:创建
ListModelMixin:查看所有
RetrieveModelMixin:查看单条
UpdateModelMixin:修改
DestroyModelMixin:删除
ListModelMixin
源码,内部仅有一个list方法用于获取全部内容
class ListModelMixin:
"""
List a queryset.
"""
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
CreateModelMixin
源码,内部有一个create方法用于创建
class CreateModelMixin:
"""
Create a model instance.
"""
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
def perform_create(self, serializer):
serializer.save()
def get_success_headers(self, data):
try:
return {'Location': str(data[api_settings.URL_FIELD_NAME])}
except (TypeError, KeyError):
return {}
RetrieveModelMixin
源码,内部仅有一个retrieve方法用于获取单条
class RetrieveModelMixin:
"""
Retrieve a model instance.
"""
def retrieve(self, request, *args, **kwargs):
instance = self.get_object()
serializer = self.get_serializer(instance)
return Response(serializer.data)
UpdateModelMixin
源码,内部有一个update方法用于修改
class UpdateModelMixin:
"""
Update a model instance.
"""
def update(self, request, *args, **kwargs):
partial = kwargs.pop('partial', False)
instance = self.get_object()
serializer = self.get_serializer(instance, data=request.data, partial=partial)
serializer.is_valid(raise_exception=True)
self.perform_update(serializer)
if getattr(instance, '_prefetched_objects_cache', None):
instance._prefetched_objects_cache = {}
return Response(serializer.data)
def perform_update(self, serializer):
serializer.save()
def partial_update(self, request, *args, **kwargs):
kwargs['partial'] = True
return self.update(request, *args, **kwargs)
DestroyModelMixin
源码,内部有一个destroy方法用于删除
class DestroyModelMixin:
"""
Destroy a model instance.
"""
def destroy(self, request, *args, **kwargs):
instance = self.get_object()
self.perform_destroy(instance)
return Response(status=status.HTTP_204_NO_CONTENT)
def perform_destroy(self, instance):
instance.delete()
基本使用
from rest_framework.generics import GenericAPIView
from rest_framework.mixins import ListModelMixin, CreateModelMixin,DestroyModelMixin,RetrieveModelMixin,UpdateModelMixin
from app01 import models
from utils import sers
class TestView(GenericAPIView, ListModelMixin, CreateModelMixin):
queryset = models.AgvTestRecord.objects.all()
serializer_class = sers.TestSer
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
九个子类视图
CreateAPIView:提供POST方法,继承自GenericApiView和CreateModelMixin
ListAPIView:提供GET方法,继承自GenericApiView和ListModelMixin
RetrieveAPIView:提供GET方法,继承自GenericApiView和RetrieveModelMixin
DestroyAPIView:提供DELETE方法,继承自GenericApiView和DestroyModelMixin
UpdateAPIView:提供PUT方法,继承自GenericApiView和UpdateModelMixin
ListCreateAPIView:提供GET和POST方法,继承自GenericApiView和ListModelMixin和CreateModelMixin
RetrieveUpdateAPIView:提供GET和PUT方法,继承自 GenericAPIview和UpdateModelMixin和RetrieveModelMixin
RetrieveDestroyAPIView:提供GET和DELETE方法,继承自GenericApiView和RetrieveModelMixin和DestroyModelMixin
RetrieveUpdateDestroyAPIView:提供GET和PUT和DELETE方法,继承自GenericApiVIew和RetrieveModelMixin和UpdateModelMixin和DestroyModelMixin
基本使用
from rest_framework.generics import CreateAPIView,ListAPIView
from app01 import models
from utils import sers
class TestView(ListAPIView, CreateAPIView):
queryset = models.AgvTestRecord.objects.all()
serializer_class = sers.TestSer
视图集
重写了as_view方法增加action参数,根据action参数决定调用方法
ViewSetMixin:视图集基类,重写了as_view()方法
ViewSet:继承ViewSetMixin和ApiView
GenericViewSet:继承ViewSetMixin和GenericApiView
ModelViewSet:继承GenericApiView和五个视图字类
ReadOnlyModelViewSet:继承RetrieveModelMixin和ListModelMixin和GenericViewSet
基本使用
from rest_framework.viewsets import ModelViewSet
from app01 import models
from utils import sers
class TestView(ModelViewSet):
queryset = models.AgvTestRecord.objects.all()
serializer_class = sers.TestSer
序列化
serializer
定义表模型
from app01 import models
class BaseModel(models.Model):
is_delete = models.BooleanField(default=False)
class Meta:
abstract = True # 设置该表为虚拟表,数据库内不创建
cclass AgvTestRecord(BaseModel):
recv_date = models.DateField(auto_now_add=True, verbose_name='接受日期')
type_choices = (("0", '朱雀'), ('1', '玄武'), ('2', '麒麟'), ('3', '其他'))
agv_type = models.CharField(max_length=32,choices=type_choices, verbose_name='车型')
agv_ip = models.CharField(max_length=32, verbose_name='ip')
agv_num = models.CharField(max_length=32, verbose_name='车架号')
version = models.CharField(max_length=32, verbose_name='版本')
result = models.BooleanField(verbose_name='测试结果')
def __str__(self):
return self.agv_ip
def agv_type_name(self):
return self.get_agv_type_display() # get_field_display()方法获得choices的中文部分
class Meta:
db_table = 'test_record' # 配置表的名族
verbose_name = '测试记录' # 别名
verbose_name_plural = verbose_name # 复数形式,默认是别名+s
class AgvQuestion(BaseModel):
happen_date = models.DateField(auto_now_add=True, verbose_name='问题日期')
description = models.CharField(max_length=64, verbose_name='问题描述')
reason = models.CharField(max_length=64, verbose_name='问题原因', null=True)
solution = models.CharField(max_length=64, verbose_name='解决方法', null=True)
solve_date = models.DateField(verbose_name='解决日期', null=True)
status = models.BooleanField(verbose_name='状态')
agv_num = models.ForeignKey('AgvTestRecord', on_delete=models.DO_NOTHING, db_constraint=False)
"""
配置db_constraint=False实现表断关联
1、表之间没有外键关联,但是有外键逻辑关联(有充当外键的字段)
2、断关联后不会影响数据库查询效率,但是会极大提高数据库增删改效率(不影响增删改查操作)
3、断关联一定要通过逻辑保证表之间数据的安全
4、断关联
5、级联关系
作者没了,详情也没:on_delete=models.CASCADE
出版社没了,书还是那个出版社出版:on_delete=models.DO_NOTHING
部门没了,员工没有部门(空不能):null=True, on_delete=models.SET_NULL
部门没了,员工进入默认部门(默认值):default=0, on_delete=models.SET_DEFAULT
"""
def __str__(self):
return self.description
@property
def agv_ip(self):
return self.agv_num.agv_ip # 深度查询
class Meta:
db_table = 'test_question'
verbose_name = '测试问题'
verbose_name_plural = verbose_name
定义序列化器
from rest_framework import serializers
反序列化
1、创建数据,需要重写父类的create方法,接受的参数为验证过的值
2、修改数据,需要重写父类的update方法,接受的参数为验证过的值
验证
1、局部验证:定义validate_字段名函数,接受该字段的值value,进行验证
2、全局验证:定义validate函数,接受request数据,进行验证
3、验证器:定义验证函数,通过参数validators配置给该字段,可配置多个
验证器
def func1():
pass
def func2():
pass
class TestSer(serializers.Serializer):
result = serializers.CharField(source='result', validators=[func1, func2]) # 使用验证器,source参数即为AgvTestRecord.result
questions = serializers.SerializerMethodField(read_only=True) # 必须配套get_字段名方法
agv_type = serializers.CharField()
agv_ip = serializers.CharField()
agv_num = serializers.CharField()
version = serializers.CharField()
def get_questions(self, obj): # 配套SerializerMethodField字段使用
temp = []
for question in obj.agvquestion_set.all():
temp.append(question.description)
return temp
# 局部验证
def validate_agv_num(self, value): # value为校验字段的值
if '666' not in value:
raise serializers.ValidationError('不能没有666!')
else:
return value
# 全局验证
def validate(self, data): # data为校验的全部数据
agv_ip= data.get('agv_ip')
agv_num= data.get('agv_num')
if agv_ip== agv_num:
raise serializers.ValidationError('不合理!')
else:
return data
# 反序列化创建数据
def create(self, validated_data):
return AgvTestRecord.objects.create(**validated_data)
# 反序列化修改数据
def update(self, instance, validated_data):
instance.agv_type= validated_data.get('agv_type')
instance.agv_ip= validated_data.get('agv_ip')
instance.agv_num= validated_data.get('agv_num')
instance.version= validated_data.get('version')
instance.result= validated_data.get('result')
instance.save()
return instance
常用字段:
BooleanField():布尔字段
CharField():字符字段
EmailField():邮箱字段
IntegerField():整数字段
DecimalField():小数字段
DateField():日期字段
常用参数:
source:如果是字段就显示字段,如果是方法就会执行方法
max_length:最大长度
min_length:最小长度
allow_blank:是否允许为空
trim_whitespace:是否截断空白字符
max_value:最大值
min_value:最小值
read_only:表明该字段仅用于序列化输出,默认为False
write_only:表面该字段仅用于反序列化输入,默认为False
required:表面该字段在反序列化时必须输入,默认为True
default:反序列化时使用的默认值
allow_null:表面该字段是否允许传入None,默认为False
label:显示字段的名称
error_messages:错误信息的字典
validators:验证器
视图函数
feom rest_framework.views import APIView
from rest_framework.response import Response
form app01 import models
from app01.ser import BookSerializer
创建Serializer对象
Serializer(instance=None, data=empty, **kwargs)
用于序列化时,将模型类对象传给instance参数
用于反序列化时,将要被反序列化的数据传给data参数
class Test1View(APIView)
# 序列化获得单个值
def get(self, request, pk, *args, **kwargs):
obj = models.AgvTestRecord.objects.filter(pk=pk)
ser_obj = TestSer(obj)
return Response(ser_obj.data)
# 反序列化修改单个值
def put(self, request, pk, *args, **kwargs):
res = {"code": 0}
obj = models.AgvTestRecord.objects.filter(pk=pk)
ser_obj = TestSer(instance=book, data=request.data, partial=True) # 设置允许部分字段更新
if ser_obj.is_valid(): # 校验数据,通过返回True,反之返回False
ser_obj.save() # 修改保存,必须重写了父类的update方法
return Response(res)
else:
res["code"] = 1
res["err"] = ser_obj.errors # 报错信息
return Response(res)
class Test2View(APIView):
# 序列化获得多个值
def get(self, request, *args, **kwargs):
obj_list = models.AgvTestRecord.objects.all()
ser_obj = TestSer(book_list, many=True) # 序列化多个信息,需要设置many=True
return Response(ser_obj.data)
# 反序列化增加单个值
def post(self, request, *args, **kwargs):
res = {"code": 0}
ser_obj = TestSer(data=request.data)
if ser_obj.is_valid(): # 校验数据,通过返回True,反之返回False
ser_obj.save() # 创建保存,必须重写了父类的create方法
return Response(res)
else:
res["code"] = 1
res["err"] = ser_obj.errors
return Response(res)
ps:
调用ser_obj.save()保存时,可以传递参数,这些数据可以在create()和update()中的validated_data参数获取到
ModelSerializer
from rest_framework import serializers
from app01 import models
class TestSer(serializers.ModelSerializer):
class Meta:
model = models.AgvTestRecord
fields = ('id', 'recv_date', 'agv_type', 'agv_type_name', 'agv_ip', 'agv_num', 'version', 'result', 'is_delete')
extra_kwargs = {
'agv_type': {'write_only': True},
'is_delete': {'write_only': True},
'agv_type_name': {'read_only': True},
'id': {'read_only': True}
}
参数:
model:指明的模型类
fields:指明生成模型类的那些字段,__all__表示全部字段,指明某些字段使用元组包起
exclude:指明排除那些字段,用法与fields相同,两者只能存在一个
extra_kwargs:添加额外参数,字典形式
认证、权限、频率
认证组件
自定义认证器
1.继承BaseAuthentication
2.重写authenticate方法
3.返回值:认证通过返回两个值,用户和任意参数,认证失败直接抛出异常
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from api import models
class ApiAuthentication(BaseAuthentication):
def authenticate(self, request):
token = request.GET.get('token')
if token:
obj = models.Token.objects.filter(token_data=token).first()
if obj:
now_time = datetime.datetime.now()
expire_date = obj.expire_date.replace(tzinfo=None)
if expire_date > now_time:
return obj, token
else:
raise AuthenticationFailed('token过期!')
else:
raise AuthenticationFailed('认证失败!')
else:
raise AuthenticationFailed('无token参数!')
局部使用
局部使用,只需要在视图类里加入:authentication_classes = [ApiAuthentication,]
from api import models
from utils import sers
from utils.utils import TestViewSet, ApiAuthentication
class TestView(TestViewSet):
authentication_classes = [ApiAuthentication,]
queryset = models.AgvTestRecord.objects.all()
serializer_class = sers.TestSer
全局使用
全局使用,在settings里配置
REST_FRAMEWORK={
"DEFAULT_AUTHENTICATION_CLASSES":["utils.utils.ApiAuthentication",]
}
权限组件
自定义权限器
1.继承BasePermission
2.重写has_permission方法
3.返回值:有权限返回True,无权限返回False
from rest_framework.permissions import BasePermission
class ApiPermission(BasePermission):
message = "必须是管理员才能访问" # 这里的message表示如果不通过权限的时候,错误提示信息
def has_permission(self, request, view):
permission = request.user.user.permission
if permission == '1':
return False
else:
return True
局部使用
局部使用,只需要在视图类里加入:permission_classes = [ApiPermission,]
from api import models
from utils import sers
from utils.utils import TestViewSet, ApiPermission
class TestView(TestViewSet):
permission_classes = [ApiPermission,]
queryset = models.AgvTestRecord.objects.all()
serializer_class = sers.TestSer
全局使用
全局使用,在settings里配置
REST_FRAMEWORK={
"DEFAULT_PERMISSION_CLASSES":["utils.utils.ApiPermission",]
}
频率组件
内置频率器
AnonRateThrottle:未登录用户ip限制,需要配合auth模块用
UserRateThrottle:登录用户频率限制,这个得配合auth模块来用
自定义频率器
1.继承SimpleRateThrottle
2.重写get_cache_key方法
3.重写scope类属性,scope与settings.py的DEFAULT_THROTTLE_RATES配合使用
3.返回值:返回的数据为限制条件,返回None及不进行限制
from rest_framework.throttling import SimpleRateThrottle
class ApiThrottle(SimpleRateThrottle):
scope = 'api'
def get_cache_key(self, request, view):
return request.META.get("REMOTE_ADDR")
局部使用
局部使用,只需要在视图类里加入:throttle_classes = [ApiThrottle,]
from api import models
from utils import sers
from utils.utils import TestViewSet, ApiThrottle
class TestView(TestViewSet):
throttle_classes = [ApiThrottle]
queryset = models.AgvTestRecord.objects.all()
serializer_class = sers.TestSer
全局使用
全局使用,在settings里配置
REST_FRAMEWORK={
'DEFAULT_THROTTLE_CLASSES':['utils.utils.ApiThrottle'],
'DEFAULT_THROTTLE_RATES':{ # 配置限制次数,局部和全局
'api':'3/m' # key与自定义的频率类的scope属性相同
}
}
过滤器、排序、分页、异常处理
过滤器
安装
pip install django-filter
使用
1.在settings里配置过滤器
2.在views里配置过滤字段
3.访问url时添加,过滤字段=过滤条件
# settings.py
REST_FRAMEWORK = {
'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',)
}
# views.py
from api import models
from utils import sers
class TestView(TestViewSet):
queryset = models.AgvTestRecord.objects.all()
serializer_class = sers.TestSer
filter_fields = ('agv_ip', 'agv_type') # 重写filter_fields属性配置过滤字段
排序器
使用
1.在views里配置排序器
2.在views里配置可排序字段
3.访问url时添加,ordering=排序字段
# views.py
from rest_framework.filters import OrderingFilter # 导入排序器
from api import models
from utils import sers
class TestView(TestViewSet):
queryset = models.AgvTestRecord.objects.all()
serializer_class = sers.TestSer
filter_backends = [OrderingFilter] # 配置排序器
ordering_fields = ('id', 'agv_num') # 配置排序字段
分页器
内置分页器
PageNumberPagination:简单分页,查看第n页,每页显示n条数据
LimitOffsetPagination:偏移分页,在n个位置,向后查看n条数据
CursorPagination:加密分页,只能查看上下页,速度最快
自定义分页器
from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination, CursorPagination
class ApiPageNumberPagination(PageNumberPagination):
"""
page_size:每页显示数目
page_query_param:前端发送的页数关键字名,默认为"page"
page_size_query_param:前端发送的每页数目关键字名,默认为None
max_page_size前端最多能设置的每页数量
"""
page_size = 5
max_page_size = 10
page_size_query_param = 'size'
class ApiLimitOffsetPagination(LimitOffsetPagination):
"""
default_limit:默认偏移大小
limit_query_param:偏移量名,偏移的大小,默认为'limit'
offset_query_param:偏移标杆名,开始偏移位置,默认为'offset'
max_limit:最大偏移限制,默认None
"""
default_limit = 5
max_limit = 10
class ApiCursorPagination(CursorPagination):
"""
page_size:每页显示数目
cursor_query_param:前端发送的页数关键字名,默认为"cursor"
ordering:排序方式
"""
page_size = 5
ordering = 'id'
局部使用
局部使用,只需要在视图类里加入:pagination_class = ApiCursorPagination
from utils.utils import ApiPageNumberPagination, ApiLimitOffsetPagination, ApiCursorPagination
from api import models
from utils import sers
class TestView(TestViewSet):
pagination_class = ApiPageNumberPagination
queryset = models.AgvTestRecord.objects.all()
serializer_class = sers.TestSer
全局使用
全局使用,在settings里配置
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'utils.utils.ApiPageNumberPagination',
}
其他使用
1.获得全部数据
2.实例化分页器
3.在数据库获得分页数据,调用paginate_queryset方法
4.对获得分页数据序列化
5.返回序列化的数据,调用get_paginated_response方法
from rest_framework.views import APIView
from api import models
from utils import sers
from utils.utils import ApiPageNumberPagination
class TestView(APIView):
def get(self,request):
obj_list = models.AgvTestRecord.objects.all() # 获得全部数据
page = ApiPageNumberPagination() # 实例化分页器
page_list = page.paginate_queryset(obj_list, request, view=self) # 在数据库获得分页数据
ser = sers.TestSer(page_list, many=True) # 序列化
return page.get_paginated_response(ser.data) # 返回序列化的数据
异常处理
异常处理器
重写exception_handler函数
1.调用原方法,得到一个结果
2.根据此结果进行二次处理
参数:
exc:报错信息
context:包含了任何额外上下文信息(例如当前被处理的视图)的字典
from rest_framework.views import exception_handler
def api_exception_handler(exc, context):
response = exception_handler(exc, context) # 调用原方法,继承器原来的处理方式,根绝结果进行二次处理
if response is None:
return ApiResponse(code=701, err=str(exc), status=status.HTTP_400_BAD_REQUEST)
else:
err = response.data.get('detail', None) # 返回的response对象的data属性即报错信息
if err is None:
err = response.data
return ApiResponse(code=response.status_code, err=err, status=status.HTTP_400_BAD_REQUEST)
使用
在settings里配置
REST_FRAMEWORK = {
'EXCEPTION_HANDLER': 'utils.utils.api_exception_handler'
}
JWT
工作原理:由头部(header).载荷(payload).签证(signature)组成
header:声明类型和加密算法,json数据,使用base64加密,可反解
payload:存放有效信息,一般包含标准注册声明,公有声明,私有声明,json数据,使用base64加密,可反解
signature:由加密后的header.加密后的payload.密钥组成,使用hash256加密,不可反解
密钥:固定字符串,存放在服务器
安装:pip install djangorestframework-jwt
内置验证
内置视图类
from rest_framework_jwt.views import ObtainJSONWebToken, VerifyJSONWebToken, RefreshJSONWebToken
ObtainJSONWebToken
VerifyJSONWebToken
RefreshJSONWebToken
内置认证类
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
JSONWebTokenAuthentication
自定义认证类
自定义jwt视图返回结果
重写jwt_response_payload_handler方法
自定义jwt认证
1.继承BaseJSONWebTokenAuthentication类
2.重写get_jwt_value方法
3.返回值:获得jwt字符串,有则返回,无则随便返回个字符串
from rest_framework_jwt.authentication import BaseJSONWebTokenAuthentication
from rest_framework.authentication import get_authorization_header
from utils import sers
def jwt_response_payload_handler(token, user=None, request=None):
"""return 想要返回的格式"""
return {
'status': 100,
'msg': 'ok',
'data': {
'token': token,
'user': sers.UserSer(user).data.get('username')
}
}
class JwtAuthentication(BaseJSONWebTokenAuthentication):
def get_jwt_value(self, request):
auth = get_authorization_header(request) # 调用get_authorization_header方法获得jwt字符串
if not auth:
return 'none' # 没有jwt字符串,随便返回个字符串,即验证失败
return auth # 有jwt字符串,返回字符串
使用
局部使用
局部使用,只需要在视图类里加入:authentication_classes = [utils.JwtAuthentication]
from app01 import models
from utils import sers
from utils import utils
class TestView(utils.TestViewSet):
authentication_classes = [utils.JwtAuthentication]
queryset = models.AgvTestRecord.objects.all()
serializer_class = sers.TestSer
全局使用
全局使用,在settings里配置
import datetime
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': ('utils.utils.JwtAuthentication',) # 自定义jwt认证类
}
JWT_AUTH = {
'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1), # 过期时间
'JWT_RESPONSE_PAYLOAD_HANDLER': 'utils.utils.jwt_response_payload_handler', # 自定义视图返回结果
}
多方式登录
1.自定义序列化类
2.自定义视图类
# sers.py
import re
from rest_framework_jwt.settings import api_settings
from app01 import models
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER # 手动签发的方法
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER # 手动签发的方法
class UserSer(serializers.ModelSerializer):
username = serializers.CharField() # user表内username是唯一字段,需要覆盖username,才能进行多方式登录
class Meta:
model = models.User
fields = ['username', 'password']
def validate(self, attrs): # 全局校验,验证用户和密码是否正确
username = attrs.get('username')
password = attrs.get('password')
re_phone = r'^1([358][0-9]|4[579]|66|7[0135678]|9[89])[0-9]{8}$'
re_email = r'^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$'
if re.match(re_phone, username):
user = models.User.objects.filter(phone=username).first()
elif re.match(re_email, username):
user = models.User.objects.filter(email=username).first()
else:
user = models.User.objects.filter(username=username).first()
if user:
if user.check_password(password):
payload = jwt_payload_handler(user) # 手动签发
token = jwt_encode_handler(payload) # 手动签发
self.context['token'] = token # 将token放入公用字典内,方便在视图里取出
self.context['username'] = user.username
return attrs
else:
raise serializers.ValidationError('密码错误!')
else:
raise serializers.ValidationError('用户名不存在!')
# views.py
from rest_framework.views import APIView
from utils import sers
from utils import utils
class Login(APIView):
def post(self, request, *args, **kwargs):
ser_obj = sers.UserSer(data=request.data)
ser_obj.is_valid(raise_exception=True)
token = ser_obj.context.get('token') # 将token从公用字典内取出
username = ser_obj.context.get('username')
return utils.ApiResponse(code=100, msg='ok', username=username, token=token)
RBAC
RBAC:基于角色的访问控制,在RBAC中,权限与角色相关联,用户通过成为适当的角色而得到该角色的权限,这极大的简化了权限管理。这种管理是层级相互依赖的,权限赋予角色,角色赋予用户。
权限六表:用户表、分组表、权限表、用户-分组表、用户-权限表、分组-权限表
使用
from django.contrib import admin
from app01 import models
from django.contrib.auth.admin import UserAdmin as DjangoUserAdmin
# 自定义User表后,admin界面管理User类
class UserAdmin(DjangoUserAdmin):
# 添加用户操作字段
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('username','password', 'is_staff', 'phone', 'groups', 'user_permissions'),
}),
)
# 展示用户呈现的字段
list_display = ('username', 'phone', 'is_staff', 'is_active', 'is_superuser')
# 使用admin注册模型表
admin.site.register(models.User, UserAdmin)
admin.site.register(models.AgvTestRecord)
本文详细介绍了Django REST FRAMEWORK(DRF)的使用,涵盖了请求与响应、路由、视图、序列化、认证、权限、频率限制、过滤器、排序、分页、异常处理、JWT以及RBAC等关键概念。讲解了如何定义和使用Request、Response,自定义路由、视图类,ModelSerializer,以及JWT的内置验证和自定义认证。同时,还涉及到了权限分配和角色控制(RBAC)的应用。
1437

被折叠的 条评论
为什么被折叠?



