1 DRF
1.1 简介
Django Rest Framework 依赖于Django框架 是构建web restful api的工具
可以快速将Django Orm 进行序列化与反序列化
提供多种类视图 Mixin扩展类 子类视图 视图集等
支持多种身份认证和权限、限流的管理方式
# 序列化:模型类对象转换成json、xml等格式化数据的过程(查询输出)
# 反序列化:json、xml等格式数据装换成模型类对象的过程(修改 删除 添加)
1.2 安装
$ pip install django
$ pip install djangorestframework
1.3 使用
INSTALLED_APPS = [
'django.contrib.admin' ,
'django.contrib.auth' ,
'django.contrib.contenttypes' ,
'django.contrib.sessions' ,
'django.contrib.messages' ,
'django.contrib.staticfiles' ,
'rest_framework' ,
'classApp' ,
]
--相关数据入库
insert into tb_group(gp_name) values ('g1'),('g2');
insert into tb_cls(cls_name, cls_group_id) values ('c1g1', 1), ('c1g2', 1), ('c1g2', 2),('c2g2', 2);
insert into tb_stu(stu_name, stu_sex, stu_scores, stu_cls_id) values ('s1', 'male', 100, 1),('s2', 'male', 100, 1),
('s3', 'female', 100, 2),('s4', 'male', 100, 2),
('s5', 'female', 100, 3),('s6', 'female', 100, 3),
('s7', 'male', 100, 4),('s8', 'male', 100, 4);
1.4 字段类型和入参
一、字段类型
1. 布尔类型
BooleanField:布尔类型(True |False )
NullBooleanField: 布尔类型(True |False |Null)
2. 字符串类型
CharField: 用于表示文本数据
EmailField:用于表示电子邮件地址
RegexField:字符串匹配特定正则表达式
SlugField: CharField子类 正则字段
URLField: 用于URL地址存储
UUIDField(format ='hex_verbose|hex|int|urn' ): UUID类型 用于表示UUID(通用唯一识别码)类型的数据 根据输入字符判断是否符合uuid格式
'hex_verbose' :"5ce0e9a5-5ffa-654b-cee0-1238041fb31a"
'hex' : "5ce0e9a55ffa654bcee01238041fb31a"
'int' : "123456789012312313134124512351145145114"
'urn' : "urn:uuid:5ce0e9a5-5ffa-654b-cee0-1238041fb31a"
IPAddressField(protocol='both' , unpack_ipv4=False , **options):IP地址类型 根据输入字符自动校验是否符合IP格式
3. 整数类型
IntegerField:整数类型 表示数字数据
FloatField:浮点数类型 表示小数数据
DecimalField(max_digits, decimal_places, coerce_to_string=None , max_value=None , min_value=None ):高精度浮点类型
-max_digits: 最多位数
-decimal_palces: 小数点位置
4. 日期时间类型
DateTimeField:日期时间类型 表示时间戳等时间数据
DateField:日期类型 表示日期数据
TimeField:时间类型 表示时间数据
DurationField:用于表示时间间隔类型的数据 可以表示一段时间的长度 根据输入的字符串转换为时间间隔类型
5. 枚举类型
ChoiceField(choices=):枚举类型 可以定义一个选择列表 用于表示一组固定的选项
MultipleChoiceField(choices):用于表示多选框类型的数据 它可以定义一个选项列表 用于表示可选的选项
6. 文本类型
FileField(max_length=None , allow_empty_file=False , use_url=UPLOADED_FILES_USE_URL):文件类型 表示文件数据
ImageField:图像类型 表示图片数据
7. 其他类型
ListField(child=None , min_length=None , max_length=None ):列表类型 表示一个列表数据
DictField(child=None ):字典类型 表示一个字典数据
二、常规入参选项
1. 通用
read_only:
默认False
用于指定字段是否只能用于序列化输出 不能用于反序列化输入
通常用于表示只读数据 在反序列化输入时 该字段的值会被忽略
write_only:
默认False
用于指定字段是否只能用于反序列化 不能用于序列化输出
在序列化输出时 该字段的值会被忽略
required:
默认True
表明该字段在反序列化时必须输入
default:
序列化和反序列化时使用的默认值
error_message:
包含错误编号与错误信息的字典
label:
用于api页面展示详情时 显示的字段名
allow_blank:
设置字段是否允许为空字符串 如果设置为True 反序列化时如果该字段的值为空字符串会被认为是有效值
validators:
设置字段的验证器 验证器是一个可调用对象 用于验证输入的数据是否符合要求
allow_null:
字段值是否允许为None
help_text:
api页面中的帮助文本信息
source:
指向实际的模型类字段名 可用于别名使用|引用其他模型类字段等
2. 特殊
max_length|min_length:
默认None
用于字符串类型
max_value|min_value:
默认None
用于整性类型
3. 其他
序列化器类的入参
context:
在序列化器类中的局部(全局)钩子、create|update方法中 可使用context获取视图类传递过来的数据
ps: 定义序列化器类的字段时 如果没有指定read_only和write_only 则这两个参数默认值都为False 表明对应的字段既在序列化时使用 也在反序列化时使用
1.5 实例
# 在目录下创建django项目
$ django-admin startproject djangoTest
$ cd djangoTest
# 在School项目下添加class应用
$ python manage.py startapp classApp
$ django-admin startapp classApp
# 使用默认sqlite数据库并做模型类迁移文件初始化
$ python manage.py makemigrations classApp
$ python manage.py migrate
# 交互式界面创建超管用户(后续各个应用站点页面登录授权需要用到>http://localhost:8000/admin/) 配置完站点可对模型类数据进行数据透视和增上改查操作
python manage.py createsuperuser
from django.db import models
class GroupInfo (models.Model):
"""年级类"""
gp_name = models.CharField(max_length=10 , verbose_name='年级名' )
class Meta :
db_table = 'tb_group'
verbose_name = '年级信息'
verbose_name_plural = verbose_name
class ClsInfo (models.Model):
cls_name = models.CharField(max_length=10 , verbose_name='班级名' )
cls_group = models.ForeignKey(to='GroupInfo' , on_delete=models.CASCADE, related_name='clsInfo' )
class Meta :
db_table = 'tb_cls'
verbose_name = '班级信息'
verbose_name_plural = verbose_name
def col_formate (self ):
"""字段预处理输出且可添加到list_display"""
return self.cls_name + "-20230101"
col_formate.short_description = "cls_name字段预处理输出"
col_formate.admin_order_field = 'id'
class StuInfo (models.Model):
"""学生类"""
SEX_CHOICE = (('male' , '男' ,),
('female' , '女' ))
stu_name = models.CharField(max_length=10 , verbose_name='学生名' )
stu_sex = models.CharField(max_length=6 , choices=SEX_CHOICE, verbose_name='性别' )
stu_scores = models.PositiveSmallIntegerField(verbose_name='语数英总分' )
stu_cls = models.ForeignKey(ClsInfo, on_delete=models.CASCADE, verbose_name='所属班级' , related_name='stuInfo' )
class Meta :
db_table = 'tb_stu'
verbose_name = '学生信息'
verbose_name_plural = verbose_name
from rest_framework import serializers
from Class.models import ClsInfo, StuInfo, GroupInfo
class GroupModelSerializer (serializers.ModelSerializer):
class Meta :
model = GroupInfo
fields = ('gp_name' ,)
class ClsInfoModelSerializer (serializers.ModelSerializer):
class Meta :
model = ClsInfo
fields = '__all__'
class StuInfoModelSerializer (serializers.ModelSerializer):
class Meta :
model = StuInfo
fields = '__all__'
from rest_framework.viewsets import ModelViewSet
from .serializers import ClsInfoModelSerializer, StuInfoModelSerializer, GroupModelSerializer
from .models import ClsInfo, StuInfo, GroupInfo
class ClsInfoViewSet (ModelViewSet ):
"""班级视图集"""
queryset = ClsInfo.objects.all ()
serializer_class = ClsInfoModelSerializer
class StuInfoViewSet (ModelViewSet ):
"""学生视图集"""
queryset = StuInfo.objects.all ()
serializer_class = StuInfoModelSerializer
class GroupInfoViewSet (ModelViewSet ):
"""年级视图集"""
queryset = GroupInfo.objects.all ()
serializer_class = GroupModelSerializer
from rest_framework.routers import DefaultRouter
from Class import views
urlpatterns = []
router = DefaultRouter()
router.register('cls' , views.ClsInfoViewSet)
router.register('stu' , views.StuInfoViewSet)
router.register('group' , views.GroupInfoViewSet)
urlpatterns += router.urls
from django.conf.urls import url, include
from django.contrib import admin
urlpatterns = [
url(r'^admin/' , admin.site.urls),
url(r'' , include('classApp.urls' )),
]
# 应用站点信息配置-classApp/admin.py
from django.contrib import admin
from .models import GroupInfo, ClsInfo, StuInfo
# 模型的管理器
class GroupInfoAdmin(admin.ModelAdmin):
list_display = ("id", "gp_name") # 显示字段
list_per_page = 50 # 每页展示记录 默认100
ordering = ("-id", ) # 排序 负号标识降序 且支持多个字段
list_editable = ['gp_name'] # 可编辑字段
fk_fields = [] # 设置显示外键字段
class ClsInfoAdmin(admin.ModelAdmin):
# 基础配置
list_display = ("id", "cls_name", "col_formate", "cls_group") # 显示字段
list_per_page = 50 # 每页展示记录 默认100
ordering = ("-id",) # 排序 负号标识降序 且支持多个字段
list_editable = ["cls_name", "cls_group"] # 可编辑字段
fk_fields = ["cls_group_id"] # 设置显示外键字段
# 操作选项
actions_on_top = True # 列表顶部显示
actions_on_bottom = False # 列表底部显示
def my_action(self, queryset):
"""自定义操作"""
queryset.update(cls_group_id=1)
my_action.short_description = "自定义action"
actions = [my_action, ] # 自定义操作
# 可在model.py添加col_formate方法 对字段预处理并作为输出列展示
# 站点增加搜索框
search_fields = ['id']
# 站点页面右侧新增过滤器
list_filter = ["id"]
# date_hierarchy = '' # 详细时间分层筛选
class StuInfoAdmin(admin.ModelAdmin):
list_display = ("id", "stu_name", "stu_sex", "stu_scores", "stu_cls") # 显示字段
list_per_page = 50 # 每页展示记录 默认100
ordering = ("-id",) # 排序 负号标识降序 且支持多个字段
list_editable = ["stu_name", "stu_sex", "stu_scores", "stu_cls"] # 可编辑字段
fk_fields = ["stu_cls_id"] # 设置显示外键字段
# 在admin中注册绑定
admin.site.register(GroupInfo, GroupInfoAdmin)
admin.site.register(ClsInfo, ClsInfoAdmin)
admin.site.register(StuInfo, StuInfoAdmin)
# 修改后台管理界面头部希纳是内容和页面标题
admin.site.site_header = 'classApp站点后台'
admin.site.title = 'classApp站点'
2 序列化器
序列化器种类:
Serializer
字段
需要和模型类匹配手写输入做后续序列化和反序列化
支持每个字段的自定义验证器validators、可重写validate_<字段名>方法对单个字段做特定校验或者重写validate方法对多个字段校验
方法
调用save()方法需要重写create()和update()方法
反序列化提供is_valid()方法验证、验证失败信息通过序列化器类实例对象.errors查看
外键相关
支持正向嵌套序列化器对象serializer 获取外键所有内容|反向嵌套通过related_name实现
支持自定义序列化的字段内容get_<字段名>
ModelSerializer
Serializer子类
字段:
基于模型类字段生成序列化器类的字段 通过在Meta中fields|exclude定义字段
支持每个字段的自定义验证器validators、可重写validate_<字段名>方法对单个字段做特定校验或者重写validate方法对多个字段校验
方法:
调用save()保存不需要重写 ModelSerializer已封装完毕 包含默认的create()和update()方法实现
外键相关:
同Serializer且支持在Meta中配置depath参数获取关联深度
ps:
序列化过程:
orm实体类对象->json|xml等结构化数据
反序列化过程:
json|xml等结构化数据->orm实体类对象
from django.db import models
class GroupInfo (models.Model):
"""年级类"""
gp_name = models.CharField(max_length=10 , verbose_name='年级名' )
class Meta :
db_table = 'group'
verbose_name = '年级信息'
def __str__ (self ):
return self.gp_name
class ClsInfo (models.Model):
cls_name = models.CharField(max_length=10 , verbose_name='班级名' )
cls_group = models.ForeignKey(to='GroupInfo' , on_delete=models.CASCADE, related_name='clsInfo' )
class Meta :
db_table = 'cls'
verbose_name = '班级信息'
class StuInfo (models.Model):
"""学生类"""
SEX_CHOICE = (('male' , '男' ,),
('female' , '女' ))
stu_name = models.CharField(max_length=10 , verbose_name='学生名' )
stu_sex = models.CharField(max_length=6 , choices=SEX_CHOICE, verbose_name='性别' )
stu_scores = models.PositiveSmallIntegerField(verbose_name='语数英总分' )
stu_cls = models.ForeignKey(ClsInfo, on_delete=models.CASCADE, verbose_name='所属班级' , related_name='stu' )
class Meta :
db_table = 'stu'
verbose_name = '学生信息'
2.1 序列化-Serializer
class GroupSerializer (serializers.Serializer):
gp_name = serializers.CharField(label='年级名' , max_length=10 )
WWWW
class StuInfoSerializer (serializers.Serializer):
SEX_CHOICE = (('male' , '男' ,),
('female' , '女' ))
stu_name = serializers.CharField(label='学生名' , max_length=10 )
stu_sex = serializers.ChoiceField(choices=SEX_CHOICE, label='性别' )
stu_scores = serializers.IntegerField(label='总分' )
stu_cls = serializers.PrimaryKeyRelatedField(label='所属班级' , read_only=True )
class ClsInfoSerializer (serializers.Serializer):
"""数据序列化器"""
id = serializers.IntegerField(label='id' , read_only=True )
cls_name = serializers.CharField(label='班名' , max_length=10 )
stu = StuInfoSerializer(many=True )
$ python manage.py shell
from classApp.models import StuInfo
from classApp.serializers import StuInfoSerializer
stu = StuInfo.objects.get(id=1) # 创建实体类对象
serializer = StuInfoSerializer(stu) # 创建序列化器对象
serializer.data # 获取序列化数据
{'stu_name': 's1', 'stu_sex': 'male', 'stu_scores': 100, 'stu_cls': 1}
2.2 反序列化-Serializer
class GroupSerializer (serializers.Serializer):
gp_name = serializers.CharField(label='年级名' , max_length=10 )
class ClsInfoSerializer (serializers.Serializer):
"""数据序列化器"""
id = serializers.IntegerField(label='id' )
cls_name = serializers.CharField(label='班名' , max_length=10 )
def standard_stu_name (val ):
if not val or not val.startswith("s" ):
raise serializers.ValidationError("stu_name不是s开头" )
return val
class StuInfoSerializer (serializers.Serializer):
SEX_CHOICE = (('male' , '男' ,),
('female' , '女' ))
stu_name = serializers.CharField(label='学生名' , max_length=10 , validators=[standard_stu_name])
stu_sex = serializers.ChoiceField(choices=SEX_CHOICE, label='性别' , required=True )
stu_scores = serializers.IntegerField(label='总分' , required=True )
stu_cls = serializers.PrimaryKeyRelatedField(label='所属班级' , many=False , queryset=ClsInfo.objects.all ())
def validated_stu_sex (self, val ):
if not val or val not in ['female' , 'male' ]:
raise serializers.ValidationError("stu_sex非法" )
return val
def validate (self, attrs ):
stu_scores = attrs.get("stu_scores" )
stu_cls = attrs.get("stu_cls" )
if stu_scores < 60 :
raise serializers.ValidationError("总分小于60非法" )
try :
if stu_cls.id > 5 :
raise serializers.ValidationError("所属班级大于5非法" )
return attrs
except Exception as e:
raise serializers.ValidationError(f"cls_id非法:{e} " )
def create (self, validated_data ):
return StuInfo.objects.create(**validated_data)
def update (self, instance, validated_data ):
instance.stu_name = validated_data.get("stu_name" , instance.stu_name)
instance.stu_sex = validated_data.get("stu_sex" , instance.stu_sex)
instance.stu_scores = validated_data.get("stu_scores" , instance.stu_scores)
instance.stu_cls = validated_data.get("stu_cls" , instance.stu_cls)
instance.save()
return instance
$ python manage.py shell
from classApp.models import StuInfo,ClsInfo
from classApp.serializers import StuInfoSerializer
# 1. 新增
valid_data = {"stu_name": "s11", "stu_sex": "female", "stu_scores": 300, "stu_cls": 2}
ser = StuInfoSerializer(instance=None, data=valid_data) # 创建序列化器对象
ser.is_valid() # 数据校验
ser.errors # 获取校验失败后的提示信息
ser.validated_data # 获取校验通过后的数据
ser.save() # 保存数据 调用序列化器对象的create方法
ser.data # 获取序列化数据
# 2. 更新
invalid_data = {"stu_name": "11", "stu_sex": "f", "stu_scores": 59, "stu_cls": 6}
stu = StuInfo.objects.get(id=1)
ser = StuInfoSerializer(instance=stu, data=invalid_data) # 创建序列化器对象
ser.is_valid() # 数据校验
ser.errors # 获取校验失败后的提示信息
# ser.validated_data
# ser.save()
# ser.data
2.3 ModelSerializer
class GroupModelSerializer (serializers.ModelSerializer):
class Meta :
model = GroupInfo
fields = ('gp_name' ,)
class ClsInfoModelSerializer (serializers.ModelSerializer):
class Meta :
model = ClsInfo
fields = '__all__'
class StuInfoModelSerializer (serializers.ModelSerializer):
class Meta :
model = StuInfo
exclude = ('stu_cls' , )
read_only_fields = ('id' , )
extra_kwargs = {
"stu_scores" : {"min_value" : 0 , "required" : True },
}
$ python manage.py shell
from classApp.serializers import StuInfoModelSerializer
ser = StuInfoModelSerializer()
ser # 根据序列化器类meta下fields或者exclude定义的字段生成对应的需要序列化或反序列化字段
3 视图
3.1 视图类
3.1.1 APIView
1. ApiView介绍
APIView是Django REST Framework提供的所有视图的基类 继承自Django的View类
APIView与View的区别:
请求对象:传入到视图中的request对象是REST framework的Request对象 而不再是Django原始的HttpRequest对象
响应对象:视图可以直接返回REST framework的Response对象 响应数据会根据客户端请求头Accpet自动转换为对应的格式进行返回
异常处理:任何APIException的子异常都会被DRF框架默认的异常处理机制处理成对应的响应信息返回给客户端
其他功能:认证、权限、限流
类属性
authentication_classes: 认证
permission_classes: 权限
throttle_classes: 限流
2. Request对象
视图继承APIView之后 传入视图的request对象是DRF框架提供的Request类的对象 Request类的对象有两个属性 request.data和request.query_params
request.data
包含解析之后的请求体数据 已经解析为了字典或类字典 相当于Django原始request对象的body|POST|FILES属性
request.query_params
包含解析之后的查询字符串数据 相当于Django原始request对象的GET属性
3. Response对象
视图继承APIView之后 响应时可以统一返回Response对象 格式如下:
from rest_framework.response import Response
response = Response(<原始响应数据>)
原始的响应数据 会根据客户端请求头的Accpet 自动转换为对应的格式并进行返回 如:
application/json
服务器会将原始响应数据转换为json数据进行返回 没指定Accept时 默认返回json
text/html
服务器会将原始响应数据转换为html网页进行返回
Class GroupSerializer(serializers.Serializer):
gp_name = serializers.CharField(label='年级名' , max_length=10 )
class ClsInfoSerializer (serializers.Serializer):
"""数据序列化器"""
id = serializers.IntegerField(label='id' )
cls_name = serializers.CharField(label='班名' , max_length=10 )
def standard_stu_name (val ):
if not val or not val.startswith("s" ):
raise serializers.ValidationError("stu_name不是s开头" )
return val
class StuInfoSerializer (serializers.Serializer):
SEX_CHOICE = (('male' , '男' ,),
('female' , '女' ))
stu_name = serializers.CharField(label='学生名' , max_length=10 , validators=[standard_stu_name])
stu_sex = serializers.ChoiceField(choices=SEX_CHOICE, label='性别' , required=True )
stu_scores = serializers.IntegerField(label='总分' , required=True )
stu_cls = serializers.PrimaryKeyRelatedField(label='所属班级' , many=False , queryset=ClsInfo.objects.all ())
def validated_stu_sex (self, val ):
if not val or val not in ['female' , 'male' ]:
raise serializers.ValidationError("stu_sex非法" )
return val
def validate (self, attrs ):
stu_scores = attrs.get("stu_scores" )
stu_cls = attrs.get("stu_cls" )
if stu_scores < 60 :
raise serializers.ValidationError("总分小于60非法" )
try :
if stu_cls.id > 5 :
raise serializers.ValidationError("所属班级大于5非法" )
return attrs
except Exception as e:
raise serializers.ValidationError(f"cls_id非法:{e} " )
def create (self, validated_data ):
return StuInfo.objects.create(**validated_data)
def update (self, instance, validated_data ):
instance.stu_name = validated_data.get("stu_name" , instance.stu_name)
instance.stu_sex = validated_data.get("stu_sex" , instance.stu_sex)
instance.stu_scores = validated_data.get("stu_scores" , instance.stu_scores)
instance.stu_cls = validated_data.get("stu_cls" , instance.stu_cls)
instance.save()
return instance
from django.conf.urls import url
from . import apiViews
urlpatterns = [
url(r'^stu$' , apiViews.StuInfoListView.as_view()),
url(r'^stu/(?P<pk>\d+)$' , apiViews.StuInfoDetailView.as_view())
]
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/' , admin.site.urls),
path(r'' , include('classApp.urls' )),
]
"""
ApiView使用
"""
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework import status
from django.http import Http404
from .serializers import StuInfoSerializer
from .models import StuInfo
class StuInfoListView (APIView ):
def get (self, request ):
"""
获取所有学生信息
:param request:
:return:
"""
queryset = StuInfo.objects.all ()
serializer = StuInfoSerializer(queryset, many=True )
return Response(serializer.data)
def post (self, request ):
"""
新增一个学生
:param request:
:return:
"""
serializer = StuInfoSerializer(data=request.data)
serializer.is_valid(raise_exception=True )
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
class StuInfoDetailView (APIView ):
"""
获取、修改、删除指定学生信息
"""
def get (self, request, pk ):
"""
获取特定学生信息
:param request:
:param pk:
:return:
"""
try :
stu = StuInfo.objects.get(pk=pk)
except StuInfo.DoesNotExist:
raise Http404
serializer = StuInfoSerializer(stu)
return Response(serializer.data)
def put (self, request, pk ):
"""
修改指定学生信息
:param request:
:param pk:
:return:
"""
try :
stu = StuInfo.objects.get(pk=pk)
except StuInfo.DoesNotExist:
raise Http404
serializer = StuInfoSerializer(stu, data=request.data)
serializer.is_valid(raise_exception=True )
serializer.save()
return Response(serializer.data)
def delete (self, request, pk ):
"""
删除指定学生信息
:param request:
:param pk:
:return:
"""
try :
stu = StuInfo.objects.get(pk=pk)
except StuInfo.DoesNotExist:
raise Http404
stu.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
3.1.2 GenericAPIView
1. GenericAPIView介绍
GenericAPIView继承自APIView 在APIView功能基础上 主要增加了操作序列化器和数据库查询的属性和方法 (ListModelMixin)支持搜索、排序、分页等功能
视图类可多继承Mixin扩展类一起使用
2. 序列化器操作相关(视图类可指定的东西)
属性:
serializer_class:声明视图类使用的序列化器
pagination_class: 声明分页控制类
PageNumberPagination: 基础分页器
LimitOffsetPagination: 偏移量分页器
CursorPagination: 游标分页器
filter_backends: 声明过滤控制后端
SearchFileter:搜索过滤器(支持的字段类型:CharField或者TextField);通过在视图类声明serach_fields对特定字段搜索?search=xxx
search_fields = ['^f1' ]
字段前置支持特殊字符匹配
^:以指定内容开始
=:全匹配
@:全文搜索(当前只支持postgreSql驱动 )
$:正则搜索
OrderingFilter:排序过滤器(通过声明ordering_fields对特定或者多个字段排序?ordering=-a,b 字段可多个用英文逗号隔开 符号表示降序)
ordering_fields = []
自定义过滤器: 1. 重写GenericAPIView类的filter_queryset()方法 2. 继承BaseFilterBackend
方法:
get_serializer_class(self):返回序列化器类 默认返回serializer_class 可以重写
get_serializer(self, args, **kwargs):返回创建序列化器类的对象 如果我们在视图中想要创建序列化器对象 可以直接调用此方法
3. 数据库查询操作相关(视图类可指定的东西)
属性:
queryset:指明视图类使用的查询集
方法:
get_queryset:返回视图使用的查询集 默认返回queryset属性 可以重写
get_object:返回从视图使用的查询集中查询指定的对象(默认根据url地址中提取的pk进行查询) 如查询不到 此方法会抛出Http404异常
get_paginated_response: 分页响应数据(除了序列化器对象的字段还有其他分页相关字段展示)
class MyPagePagination (PageNumberPagination ):
page_query_param = 'page'
page_size = 3
page_size_query_param = 'page_size'
max_page_size = 5
class StuInfoListView (GenericAPIView ):
serializer_class = StuInfoSerializer
queryset = StuInfo.objects.all ()
pagination_class = MyPagePagination
def get (self, request ):
"""
获取所有的学生信息数据
:param request:
:return:
"""
queryset = self.get_queryset()
page = self.paginate_queryset(queryset)
serializer = self.get_serializer(page, many=True )
return Response(serializer.data)
def post (self, request ):
"""
新增一个学生
:param request:
:return:
"""
serializer = self.get_serializer(data=request.data)
if serializer.is_valid(raise_exception=True ):
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
else :
return Response({"msg" : "fail" }, status=status.HTTP_400_BAD_REQUEST)
class StuInfoDetailView (GenericAPIView ):
serializer_class = StuInfoSerializer
queryset = StuInfo.objects.all ()
def get (self, request, pk ):
"""
获取指定学生信息
:param request:
:param pk:
:return:
"""
instance = self.get_object()
serializer = StuInfoSerializer(instance)
return Response(serializer.data)
def put (self, request, pk ):
"""
修改指定学生信息
:param request:
:param pk:
:return:
"""
instance = self.get_object()
serializer = StuInfoSerializer(instance, data=request.data)
serializer.is_valid(raise_exception=True )
serializer.save()
return Response(serializer.data)
def delete (self, request, pk ):
"""
删除指定学生
:param request:
:param pk:
:return:
"""
instance = self.get_object()
instance.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
3.1.3 Mixin扩展类
Mixin扩展类
ListModelMixin
list (request,*args,**kwargs):
封装获取一组数据操作
数据支持过滤和分页
CreateModelMixin
create():
封装新增一条数据操作
RetrieveModelMixin
retrieve():
封装获取指定数据操作
UpdateModelMixin
update():
封装更新指定数据操作
DestroyModelMixin
destroy():
封装删除指定数据操作
Mixin扩展类相关源码
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 {}
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)
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)
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)
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()
class MyPagePagination (PageNumberPagination ):
page_query_param = 'page'
page_size = 3
page_size_query_param = 'pageSize'
max_page_size = None
class StuInfoListView (GenericAPIView, CreateModelMixin, ListModelMixin):
serializer_class = StuInfoSerializer
queryset = StuInfo.objects.all ()
pagination_class = MyPagePagination
filter_backends = [SearchFilter, OrderingFilter]
search_fields = ['stu_name' , ]
ordering_fields = ['stu_scores' ]
def get (self, request ):
"""
获取所有的学生信息数据
:param request:
:return:
"""
return self.list (request)
def post (self, request ):
"""
新增一个学生
:param request:
:return:
"""
return self.create(request)
class StuInfoDetailView (GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
serializer_class = StuInfoSerializer
queryset = StuInfo.objects.all ()
def get (self, request, pk ):
"""
获取指定学生信息
:param request:
:param pk:
:return:
"""
return self.retrieve(request, pk)
def put (self, request, pk ):
"""
修改指定学生信息
:param request:
:param pk:
:return:
"""
return self.update(request, pk)
def delete (self, request, pk ):
"""
删除指定学生
:param request:
:param pk:
:return:
"""
return self.destroy(request, pk)
3.1.4 子类视图
子类视图类 直接对请求方法GET|POST|PUT|DELETE封装 不用再显式声名
子类视图类
ListAPIView
继承:
GenericAPIView|ListModelMixin
封装好get请求->get()方法-全量
CreateAPIView
继承:
GenericAPIView|CreateModelMixin
封装post请求->post()方法
RetrieveAPIView
继承:
GenericAPIView、RetrieveModelMixin
封装get请求->get(pk=)方法-特定
UpdateAPIView
继承:
GenericAPIView、UpdateModelMixin
封装put请求->put()方法
DestroyAPIView
继承:
GenericAPIView、DestroyModelMixin
封装delete请求->delete()方法
ListCreateAPIView
继承:
GenericAPIView、ListModelMixin、CreateModelMixin
封装get、post请求->get()&post()方法
RetrieveUpdateAPIView
继承:
GenericAPIView、RetrieveModelMixin、UpdateModelMixin
封装get、put请求->get()&put()方法
RetrieveDestroyAPIView
继承:
GenericAPIView、RetrieveModelMixin、DestroyModelMixin
封装get、delete请求->get()&delete()方法
RetrieveUpdateDestroyAPIView
继承:
GenericAPIView、RetrieveModelMixin、UpdateModelMixin、DestroyModelMixin
封装get、put、delete请求->get()&put()&delete()方法
class MyPagePagination (PageNumberPagination ):
page_query_param = 'page'
page_size = 3
page_size_query_param = 'pageSize'
max_page_size = 5
class StuInfoListView (ListCreateAPIView ):
serializer_class = StuInfoSerializer
queryset = StuInfo.objects.all ()
pagination_class = MyPagePagination
filter_backends = [SearchFilter, OrderingFilter]
search_fields = ['stu_name' , ]
ordering_fields = ['stu_scores' ]
class StuInfoDetailView (RetrieveUpdateDestroyAPIView ):
serializer_class = StuInfoSerializer
queryset = StuInfo.objects.all ()
3.2 视图集
视图集
简介:
将操作同一组资源相关的处理方法放在同一个类中 这个类叫做视图集 精简类视图使用且URL配置需要映射请求方法和视图集方法
父类:
ViewSet(继承ViewSetMixin&APIView 和APIView类似 提供认证|权限|限流等)
GenericViewSet(继承GenericAPIView&ViewSetMixin 可搭配Mixin扩展类使用)
ModelViewSet(继承GenericViewSet且包含ListModelMixin|CreateModelMixin|RetrieveModelMixin|UpdateModelMixin|DestroyModelMixin)
ReadOnlyModelViewSet(继承GenericViewSet且包含ListModelMixin|RetrieveModelMixin)
方法:
list :获取数据
create:新增数据
retrieve:获取特定的数据
update:更新特定的数据
destroy:删除特定的数据
支持自定义
PS: 可通过重写方法实现特定字段展示
使用:
1. 继承视图集相关父类
2. 视图集中方法通过list |create|update|destroy等映射请求方式(get|post|put|delete)等
3. URL配置 需要指定请求方法和对应视图集方法映射关系
PS: 学生API管理需要2 个类视图StuInfoListView&StuInfoDetailView但是视图集一个StuInfoViewSet就能实现
3.2.1 视图集实例
from django.conf.urls import url
from . import myViewSet
urlpatterns = [
url(r'^stu$' , myViewSet.StuInfoViewSet.as_view({
'get' : 'list' ,
'post' : 'create' ,
})),
url(r'^stu/(?P<pk>\d+)$' , myViewSet.StuInfoViewSet.as_view({
'get' : 'retrieve' ,
'put' : 'update' ,
'delete' : 'destroy'
})),
url(r'^stu/last$' , myViewSet.StuInfoViewSet.as_view({
'get' : 'last'
}))
]
from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet
from .serializers import StuInfoSerializer
from .models import StuInfo
class StuInfoViewSet (ModelViewSet ):
serializer_class = StuInfoSerializer
queryset = StuInfo.objects.all ()
def last (self, request ):
"""
自定义方法-获取最后一条记录
:param request:
:return:
"""
stu = StuInfo.objects.last()
serializer = self.get_serializer(stu)
return Response(serializer.data)
3.2.2 路由配置优化
路由配置优化目的是为了优化视图集action方法和请求方式的映射关系 动态管理url配置项
相关类
SimpleRouter
DefaultRouter
使用
router = SimpleRouter()
router.register(prefix=,viewset=,base_name=)
-prefix:该视图集所有处理函数url地址的统一前缀
-viewset:视图集
-base_name:该视图集所有处理函数路由name的统一前缀
ps: 通过可以通过action装饰器修饰视图集相关action方法(create|list |retrieve|update|destroy等)
action装饰器相关入参:
methods:list ->表明该处理方法对应的请求方式
detail:boolean->表明生成url配置项时 是否需要从路径参数中提取pk数据
*URL路径需要/结尾 否则会出现301 跳转情况即 api会请求2 次(301 ->200 ):xxx->xxx/
from rest_framework.routers import DefaultRouter,SimpleRouter
from . import myViewSet
urlpatterns = []
router = SimpleRouter()
router.register(prefix='stu' , viewset=myViewSet.StuInfoViewSet, basename='stu' )
urlpatterns += router.urls
for _url in router.urls:
print (_url)
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet
from .serializers import StuInfoSerializer
from .models import StuInfo
class StuInfoViewSet (ModelViewSet ):
serializer_class = StuInfoSerializer
queryset = StuInfo.objects.all ()
lookup_value_regex = '\d+'
@action(methods=["get" ], detail=False )
def last (self, request ):
"""
自定义方法-获取最后一条记录
:param request:
:return:
"""
stu = StuInfo.objects.last()
serializer = self.get_serializer(stu)
return Response(serializer.data)
4 扩展功能
4.1 文档
4.1.1 django-rest-swagger
pip install django-rest-swagger
# https://django-rest-swagger.readthedocs.io/en/latest/
INSTALLED_APPS = [
'rest_framework_swagger'
]
SWAGGER_SETTINGS = {
'SECURITY_DEFINITIONS' : {
"basic" : {
'type' : 'basic'
}
},
'USE_SESSION_AUTH' : True ,
'LOGIN_URL' : 'rest_framework:login' ,
'LOGOUT_URL' : 'rest_framework:logout' ,
'APIS_SORTER' : 'alpha' ,
'DOC_EXPANSION' : None ,
'JSON_EDITOR' : True ,
'OPERATIONS_SORTER' : None ,
'SHOW_REQUEST_HEADERS' : True ,
}
TEMPLATES = [
{
'BACKEND' : 'django.template.backends.django.DjangoTemplates' ,
'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' ,
],
'libraries' : {
'staticfiles' : 'django.templatetags.static' ,
},
},
},
]
from rest_framework import permissions
from rest_framework.schemas import get_schema_view
from rest_framework_swagger.renderers import SwaggerUIRenderer, OpenAPIRenderer
schema_view = get_schema_view(title='DocApi' ,
renderer_classes=[SwaggerUIRenderer,
OpenAPIRenderer
],
authentication_classes=(),
permission_classes=(permissions.AllowAny, ),
)
urlpatterns = [
path('admin/' , admin.site.urls),
url(r'^api-auth/' , include('rest_framework.urls' , namespace='rest_framework' ), name='auth' ),
path('swagger/' , schema_view, name='swagger' ),
]
4.1.2 coreApi
pip install coreapi
# https://www.coreapi.org/
REST_FRAMEWORK = {
'DEFAULT_SCHEMA_CLASS' : 'rest_framework.schemas.coreapi.AutoSchema' ,
}
from django.conf.urls import url
from django.contrib import admin
from django.urls import path, include
from rest_framework.documentation import include_docs_urls
urlpatterns = [
path('admin/' , admin.site.urls),
url(r'^coreApi/' , include_docs_urls(title="apiCore" )),
path(r'' , include('classApp.urls' )),
]
'''
<apiCore "http://localhost:8000/coreApi/">
stu: {
list()
create(stu_name, stu_sex, stu_scores, stu_cls)
last()
read(id)
update(id, stu_name, stu_sex, stu_scores, stu_cls)
partial_update(id, [stu_name], [stu_sex], [stu_scores], [stu_cls])
delete(id)
}
'''
4.1.3 drf-yasg2
drf-yasg2特色:
完全支持嵌套序列化器和模式(Full support for nested Serializers and Schemas)
响应模式和描述(Response schemas and descriptions)
与编码工具兼容的模型定义(Model definitions compatible with codegen tools)
定制在规范生成过程的所有点上都挂钩(Customization hooks at all points in the spec generation process)
JSON和YAML格式的规范(JSON and YAML format for spec)
捆绑最新版本的swagger-ui和redoc,用于查看生成的文档(Bundles latest version of swagger-ui and redoc for viewing the generated documentation)
架构视图是开箱即用的(Schema view is cacheable out of the box)
生成的Swagger模式可以通过Swagger-spec-validator自动验证(Generated Swagger schema can be automatically validated by swagger-spec-validator)
通过URLPathVersioning和NamespaceVersioning支持Django REST 框架API版本控制;目前不支持其他DRF或自定义版本控制方案(Supports Django REST Framework API versioning with URLPathVersioning and NamespaceVersioning; other DRF or custom versioning schemes are not currently supported)
ps:
URL: https://pypi.org/project/drf-yasg2/
pip install drf-yasg2[validation] 扩展Swagger-spec-validator
截至到20230425 drf-yasg2==1.19 .4 最高设配的django==3.1 djangorestframework==3.12
pip install drf-yasg2==1.19.4
pip install django==3.1 djangorestframework==3.12
INSTALLED_APPS = [
'drf_yasg2' ,
]
from django.conf.urls import url
from django.contrib import admin
from django.urls import path, include, re_path
from rest_framework import permissions
from drf_yasg2.views import get_schema_view
from drf_yasg2 import openapi
drf_yasg2_schema_view = get_schema_view(
openapi.Info(
title='drfYasg2-SwaggerUi' ,
default_version="v1" ,
description="SwaggerUi-yasg2" ,
terms_of_service="https://www.google.com/policies/terms/" ,
contact=openapi.Contact(email="xxx@163.com" ),
license=openapi.License(name="BSD License" )
),
public=True ,
permission_classes=(permissions.AllowAny, )
)
urlpatterns = [
path('admin/' , admin.site.urls),
path(r'' , include('classApp.urls' )),
re_path(r'^swagger2(?P<format>\.json|\.yaml)$' , drf_yasg2_schema_view.without_ui(cache_timeout=0 )),
path('swagger2/' , drf_yasg2_schema_view.with_ui('swagger' , cache_timeout=0 ), name='schema-swagger-ui2' ),
path('redoc2/' , drf_yasg2_schema_view.with_ui('redoc' , cache_timeout=0 ), name='schema-redoc2' )
]
4.2 认证
****局部配置优先级高于全局配置
DRF框架认证方案:
BasicAuthentication: 基于HTTP Basic Authentication协议实现的认证方式 用户需要在请求头中携带base64编码后的用户名和密码(生产环境只建议https协议)
SessionAuthentication: 基于django默认的session会话机制实现的认证方式 在使用时需要先登录获取sessionid 然后发送请求时携带该sessionid和csrftoken
TokenAuthentication: 基于Token的认证方式 用户需要先通过用户相关信息(账号+密码)获取一个Token 然后在每次请求时携带该Token进行认证 token保存于请求头Authorization
自定义认证: 通过继承BaseAuthentication 重写authenticate方法实现
JWTAuthentication:
基于JsonWebToken(JWT)的认证方式 具有无状态 可扩展性高等优点(第三方扩展包)
pip install djangorestframework-simplejwt[crypto]
OAuth2Authentication:
drf框架OAuth包为REST框架提供了OAuth1和OAuth2支持
pip install django-oauth-toolkit
使用:
1. 全局配置(settings.py):
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES' : [
'rest_framework.authentication.BasicAuthentication' ,
]
}
2. 局部配置(视图类.py)
视图类类属性authentication_classes = [] 或者@permission_classes装饰器
方式一:
class MyAuthView (APIView ):
authentication_classes = [SessionAuthentication, BasicAuthentication]
permission_classes = [IsAuthenticated]
def get (self, request, format =None ):
content = {
'user' : str (request.user),
'auth' : str (request.auth),
}
return Response(content)
方式二:
@api_view(['GET' ] )
@authentication_classes([SessionAuthentication, BasicAuthentication] )
@permission_classes([IsAuthenticated] )
def auth_view (request, format =None ):
content = {
'user' : str (request.user),
'auth' : str (request.auth),
}
return Response(content)
PS: https://www.django-rest-framework.org/api-guide/authentication/
4.2.1 SessionAuthentication
REST_FRAMEWORK = {
'DEFAULT_SCHEMA_CLASS' : 'rest_framework.schemas.coreapi.AutoSchema' ,
'DEFAULT_AUTHENTICATION_CLASSES' : [
'rest_framework.authentication.SessionAuthentication' ,
],
'DEFAULT_PERMISSION_CLASSES' : ['rest_framework.permissions.IsAuthenticated' , ]
}
4.2.2 BasicAuthentication
REST_FRAMEWORK = {
'DEFAULT_SCHEMA_CLASS' : 'rest_framework.schemas.coreapi.AutoSchema' ,
'DEFAULT_AUTHENTICATION_CLASSES' : [
'rest_framework.authentication.BasicAuthentication' ,
],
'DEFAULT_PERMISSION_CLASSES' : ['rest_framework.permissions.IsAuthenticated' ]
}
4.2.3 自定义认证
REST_FRAMEWORK = {
'DEFAULT_SCHEMA_CLASS' : 'rest_framework.schemas.coreapi.AutoSchema' ,
'DEFAULT_AUTHENTICATION_CLASSES' : [
'utils.myAuth.MyInitAuthentication' ,
],
'DEFAULT_PERMISSION_CLASSES' : ['rest_framework.permissions.IsAuthenticated' ]
}
from django.contrib.auth.models import User
from rest_framework.authentication import BaseAuthentication
from rest_framework import exceptions
class MyInitAuthentication (BaseAuthentication ):
"""自定义认证"""
def authenticate (self, request ):
username = request.GET.get('myAuthKey' )
if not username:
return None
try :
user = User.objects.get(username=username)
except User.DoesNotExist:
raise exceptions.AuthenticationFailed('invalidUser' )
return (user, None )
4.2.4 TokenAuthentication
INSTALLED_APPS = [
'rest_framework.authtoken' ,
]
REST_FRAMEWORK = {
'DEFAULT_SCHEMA_CLASS' : 'rest_framework.schemas.coreapi.AutoSchema' ,
'DEFAULT_AUTHENTICATION_CLASSES' : [
'rest_framework.authentication.TokenAuthentication' ,
],
'DEFAULT_PERMISSION_CLASSES' : ['rest_framework.permissions.IsAuthenticated' ]
}
$ python manage.py migrate
from rest_framework.authtoken import views as _builtInAuthViews
urlpatterns = [
path('admin/' , admin.site.urls),
path('api-token-auth/' , _builtInAuthViews.obtain_auth_token)
]
{"Authorization" : "Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b" }
4.2.4 JWTAuthentication
1. 安装
$ pip install djangorestframework-simplejwt[crypto]
2. 相关认证方案
JWTAuthentication
JWTStatelessUserAuthentication(JWTTokenUserAuthentication)
3. 相关文档
# https://django-rest-framework-simplejwt.readthedocs.io/en/latest/
4. 扩展(可选)
若需要持久化token相关信息
INSTALLED_APPS = [
'rest_framework_simplejwt', # simplejwt应用注册
]
INSTALLED_APPS = [
'rest_framework_simplejwt' ,
]
REST_FRAMEWORK = {
'DEFAULT_SCHEMA_CLASS' : 'rest_framework.schemas.coreapi.AutoSchema' ,
'DEFAULT_AUTHENTICATION_CLASSES' : [
'rest_framework_simplejwt.authentication.JWTAuthentication' ,
],
'DEFAULT_PERMISSION_CLASSES' : [
'rest_framework.permissions.IsAuthenticated' ,
]
}
from datetime import timedelta
SIMPLE_JWT = {
"ACCESS_TOKEN_LIFETIME" : timedelta(minutes=1 ),
"REFRESH_TOKEN_LIFETIME" : timedelta(days=1 ),
"ROTATE_REFRESH_TOKENS" : False ,
"BLACKLIST_AFTER_ROTATION" : False ,
"UPDATE_LAST_LOGIN" : False ,
"ALGORITHM" : "HS256" ,
"SIGNING_KEY" : SECRET_KEY,
"VERIFYING_KEY" : "" ,
"AUDIENCE" : None ,
"ISSUER" : None ,
"JSON_ENCODER" : None ,
"JWK_URL" : None ,
"LEEWAY" : 0 ,
"AUTH_HEADER_TYPES" : ("Bearer" ,),
"AUTH_HEADER_NAME" : "HTTP_AUTHORIZATION" ,
"USER_ID_FIELD" : "id" ,
"USER_ID_CLAIM" : "user_id" ,
"USER_AUTHENTICATION_RULE" : "rest_framework_simplejwt.authentication.default_user_authentication_rule" ,
"AUTH_TOKEN_CLASSES" : ("rest_framework_simplejwt.tokens.AccessToken" ,),
"TOKEN_TYPE_CLAIM" : "token_type" ,
"TOKEN_USER_CLASS" : "rest_framework_simplejwt.models.TokenUser" ,
"JTI_CLAIM" : "jti" ,
"SLIDING_TOKEN_REFRESH_EXP_CLAIM" : "refresh_exp" ,
"SLIDING_TOKEN_LIFETIME" : timedelta(minutes=5 ),
"SLIDING_TOKEN_REFRESH_LIFETIME" : timedelta(days=1 ),
"TOKEN_OBTAIN_SERIALIZER" : "rest_framework_simplejwt.serializers.TokenObtainPairSerializer" ,
"TOKEN_REFRESH_SERIALIZER" : "rest_framework_simplejwt.serializers.TokenRefreshSerializer" ,
"TOKEN_VERIFY_SERIALIZER" : "rest_framework_simplejwt.serializers.TokenVerifySerializer" ,
"TOKEN_BLACKLIST_SERIALIZER" : "rest_framework_simplejwt.serializers.TokenBlacklistSerializer" ,
"SLIDING_TOKEN_OBTAIN_SERIALIZER" : "rest_framework_simplejwt.serializers.TokenObtainSlidingSerializer" ,
"SLIDING_TOKEN_REFRESH_SERIALIZER" : "rest_framework_simplejwt.serializers.TokenRefreshSlidingSerializer" ,
}
from django.contrib import admin
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView, TokenVerifyView
urlpatterns = [
path('admin/' , admin.site.urls),
path('api/token/' , TokenObtainPairView.as_view(), name='token_obtain_pair' ),
path('api/token/refresh/' , TokenRefreshView.as_view(), name='token_refresh' ),
path('api/token/verify/' , TokenVerifyView.as_view(), name='token_verify' )
]
4.2.5 OAuth2Authentication
1. 安装
pip install django-oauth-toolkit==2.2.0
2. 文档
https://django-oauth-toolkit.readthedocs.io/en/latest/install.html
3. oauth授权方案
Authorization Code(授权码-通过授权码获取accessToken)(√)
PKCE(授权码的扩展-支持csrf和授权码注入防护)
Client Credentials(客户端证书)(√)
Device Code(设备编码)
Refresh Token(刷新令牌)
ps: 实际需要通过以上某种方案获取到对应的accessToken
现阶段只研究授权码和客户端证书
4. 可全局配置oauth2相关参数(https://django-oauth-toolkit.readthedocs.io/en/latest/settings.html)
OAUTH2_PROVIDER = {
'SCOPES': {
'read': 'Read scope',
'write': 'Write scope',
},
'CLIENT_ID_GENERATOR_CLASS': 'oauth2_provider.generators.ClientIdGenerator',
'ACCESS_TOKEN_EXPIRE_SECONDS': 3600, # token有效期
'AUTHORIZATION_CODE_EXPIRE_SECONDS': 60 # 授权码有效期
}
1. 配置settings.py
INSTALLED_APPS = [
'oauth2_provider' ,
]
REST_FRAMEWORK = {
'DEFAULT_SCHEMA_CLASS' : 'rest_framework.schemas.coreapi.AutoSchema' ,
'DEFAULT_AUTHENTICATION_CLASSES' : [
'oauth2_provider.contrib.rest_framework.OAuth2Authentication' ,
],
'DEFAULT_PERMISSION_CLASSES' : [
'rest_framework.permissions.IsAuthenticated' ,
]
}
LOGIN_URL='/admin/login/'
2. 同步到数据库
$ python manage.py migrate
3. 配置路由urls.py
from django.contrib import admin
from django.urls import include, path
urlpatterns = [
path('admin/' , admin.site.urls),
path('o/' , include('oauth2_provider.urls' , namespace='oauth2_provider' )),
]
4. 授权认证
4.1 授权码形式
4.1 .1 浏览器访问配置应用授权码
http://127.0 .0 .1 :8000 /o/applications/register/
4.1 .2 记录client_id和client_secret
client_id:
9omo0yWi2Cx3wJuI37uCLSLkesNnosyRxsJv8iNp
client_secret:
GJEmxnW7nUBDq3M8OAOqfdvKasMaNyrfgKL6Vx6lUNwjn61PzyZDH1CQkppoOOHOzEokGL61VQ1Yk2AJT1fJxskLUqtw3mduhtgFdbkvjE93K7NGJcHG4uLgNUZYLSah
4.1 .3 sha256生成特定code_verifier和code_challenge
import random
import string
import base64
import hashlib
code_verifier = '' .join(random.choice(string.ascii_uppercase + string.digits) for _ in range (random.randint(43 , 128 )))
code_verifier = base64.urlsafe_b64encode(code_verifier.encode('utf-8' ))
code_challenge = hashlib.sha256(code_verifier).digest()
code_challenge = base64.urlsafe_b64encode(code_challenge).decode('utf-8' ).replace('=' , '' )
if __name__ == '__main__' :
print ("code_verifier=" , code_verifier)
print ("code_challenge=" , code_challenge)
code_verifier= b'T0ZDTk1FRVc0VEk1MUxKVEFJSEZXMjVTOEU5NVg3UEFaNzJNN0FDWUU5TDBGR0E2NjA5RkZQVVU4NzgySjk4QUtQRUlBUlVYMjExQVVOWllLR0dNQg=='
code_challenge= tHx4MQUVgiMgVj6ykcuGDSLXy4xlhuIX-Q28RvQiBAA
4.1 .4 浏览器访问获取授权码(302 跳转会返回对应的code)
http://127.0 .0 .1 :8000 /o/authorize/?response_type=code&code_challenge=tHx4MQUVgiMgVj6ykcuGDSLXy4xlhuIX-Q28RvQiBAA&code_challenge_method=S256&client_id=9omo0yWi2Cx3wJuI37uCLSLkesNnosyRxsJv8iNp&redirect_uri=http://127.0 .0 .1 :8000 /noexist/callback
4.1 .5 授权码信息
http://127.0 .0 .1 :8000 /noexist/callback?code=HvW8gYoU4SrzPevnvlqnIhkrYuHDHb
4.1 .6 oauth2-授权码形式认证-验证(即获取认证token)
url:
http://127.0 .0 .1 :8000 /o/token/
Header:
application/x-www-form-urlencoded
data:
client_id:9omo0yWi2Cx3wJuI37uCLSLkesNnosyRxsJv8iNp
client_secret:GJEmxnW7nUBDq3M8OAO省略
code:HvW8gYoU4SrzPevnvlqnIhkrYuHDHb
code_verifier:T0ZDTk1FRVc0VEk1MUxKVEFJSEZXMjVTOEU5省略
redirect_uri:http://127.0 .0 .1 :8000 /noexist/callback
grant_type:authorization_code
Response:
{
"access_token" : "leAa0ErWZB39NElDEYwM2O4OCpnagB" ,
"expires_in" : 36000 ,
"token_type" : "Bearer" ,
"scope" : "read write" ,
"refresh_token" : "oBVCInmLAmKA8tIyhBqZh2sV8yPaa7"
}
4.1 .7 授权页面访问每次携带请求头Authorization: Bearer leAa0ErWZB39NElDEYwM2O4OCpnagB
curl -X GET http://localhost:8000 /stu/ -H 'Authorization: Bearer leAa0ErWZB39NElDEYwM2O4OCpnagB'
5. 扩展-可自定义users类
$ python manage.py startapp users
----users/models.py
from django.contrib.auth.models import AbstractUser
class MyUser (AbstractUser ):
pass
INSTALLED_APPS = [
'users' ,
]
AUTH_USER_MODEL='users.MyUser'
$ python manage.py makemigrations
$ python manage.py migrate
4.3 权限
1. 权限类别:
1.1 IsAuthenticated: 仅允许已经认证的用户访问
1.2 AllowAny: 允许任何人访问
1.3 IsAdminUser: 仅允许管理员用户访问(保证user.is_staff=True )
1.4 IsAuthenticatedOrReadOnly: 对于未认证的用户只允许进行读取操作(GET|HEAD|OPTIONS) 已认证用户可以执行任意操作
1.5 DjangoModelPermissions: 基于Django模型的权限控制(django.contrib.authmodel) 与Django admin中的权限控制类似 此权限只能应用于具有.queryset属性集的视图 只有在用户通过身份验证并分配了相关模型权限的情况下 才会被授予权限
POST 请求要求用户对模型具有添加权限
PUT|PATCH 请求要求用户对模型具有更改权限
DELETE 请求想要求用户对模型具有删除权限
要使用自定义模型权限 覆盖DjangoModelPermissions并设置.perms_map属性
1.6 DjangoObjectPermissions: 基于Django模型对象的权限控制
此权限类与Django的标准对象权限框架相关联 该框架允许模型上的每个对象的权限 为了使用此权限类 你还需要添加支持对象级权限的权限后端 例如django-guardian
与DjangoModelPermissions一样 此权限只能应用于具有.queryset属性或.get_queryset()方法的视图 只有在用户通过身份验证并且具有相关的每个对象权限和相关的模型权限后 才会被授予权限
POST 请求要求用户对模型实例具有添加权限
PUT 和PATCH 请求要求用户对模型示例具有更改权限
DELETE 请求要求用户对模型示例具有删除权限
请注意,DjangoObjectPermissions 不需要 django-guardian软件包,并且应当同样支持其他对象级别的后端
与DjangoModelPermissions一样 你可以通过重写DjangoObjectPermissions并设置.perms_map属性来使用自定义模型权限
1.7 自定义权限
要实现自定义权限 继承BasePermission并实现以下方法中的一个或两个
.has_permission(self, request, view)
.has_object_permission(self, request, view, obj)
如果请求被授予访问权限 方法应该返回True 否则返回False
如果你需要测试请求是读取操作还是写入操作 则应该根据常量SAFE_METHODS检查请求方法 SAFE_METHODS是包含'GET' 'OPTIONS' 'HEAD' 的元组 例如:
if request.method in permissions.SAFE_METHODS:
else :
eg:
class TestPermission (BasePermission ):
def has_permission (self, request, view ):
'''修饰的视图类是否有访问权限'''
return True
def has_object_permission (self, request, view, obj ):
'''修饰的视图类是否有对某个实体orm对象有访问权限'''
return True
class TestViewSet (ReadOnlyModelViewSet ):
'''视图类'''
queryset = Stu.object .all ()
serializer_class = StuInfoSerializer
permission_classes = [TestPermission]
2. 使用(通常和认证一起使用):
2.1 全局配置
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES' : [
'rest_framework.permissions.IsAuthenticatedOrReadOnly' ,
}
2.2 局部配置
视图类指定类属性permission_classes = [IsAuthenticated]
REST_FRAMEWORK = {
'DEFAULT_SCHEMA_CLASS' : 'rest_framework.schemas.coreapi.AutoSchema' ,
'DEFAULT_AUTHENTICATION_CLASSES' : [
'rest_framework.authentication.TokenAuthentication' ,
],
'DEFAULT_PERMISSION_CLASSES' : [
'rest_framework.permissions.IsAuthenticatedOrReadOnly' ,
]
}
4.4 限流
1. 限流类:
AnonRateThrottle: 适用于任何用户(即使未登录的匿名用户)的限制类
UserRateThrottle: 适用于认证通过后的用户的限制类
ScopedRateThrottle: 适用于对不同视图类的特定资源的扩展访问限制类(视图类需要指定throttle_scope属性才生效)
自定义限流类:继承BaseThrottle|SimpleRateThrottle|UserRateThrottle其一 重写allow_request()方法
2. 使用:
全局配置
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES' : [
'rest_framework.throttling.AnonRateThrottle' ,
'rest_framework.throttling.UserRateThrottle' ,
'rest_framework.throttling.ScopedRateThrottle' ],
'DEFAULT_THROTTLE_RATES' : {
'anon' : '1/minute' ,
'user' : '2/minute' ,
},
}
局部配置
视图类指定throttle_classes属性
视图函数通过@throttle_classes([])修饰
3. ps:
当限制的维度是多个 即每分钟|每小时都有限制时
可通过自定义限流类 并指定属性scope 并在REST_FRAMEWORK.DEFAULT_THROTTLE_RATES配置scope指定的限流维度
class TestRateThrottle (UserRateThrottle ):
scope = 'haha'
'DEFAULT_THROTTLE_RATES' : {
'haha' : '2/hour' ,
},
REST_FRAMEWORK = {
'DEFAULT_SCHEMA_CLASS' : 'rest_framework.schemas.coreapi.AutoSchema' ,
'DEFAULT_AUTHENTICATION_CLASSES' : [
'oauth2_provider.contrib.rest_framework.OAuth2Authentication' ,
],
'DEFAULT_PERMISSION_CLASSES' : [
'rest_framework.permissions.IsAuthenticated' ,
],
'DEFAULT_THROTTLE_CLASSES' : [
'rest_framework.throttling.AnonRateThrottle' ,
'rest_framework.throttling.UserRateThrottle' ,
'rest_framework.throttling.ScopedRateThrottle' ],
'DEFAULT_THROTTLE_RATES' : {
'anon' : '1/minute' ,
'user' : '2/minute' ,
},
}
4.5 过滤
# 1. 安装
pip install django-filter==2.4.0
# 2. 过滤器类
SearchFileter:搜索过滤器(支持的字段类型:CharField或者TextField);通过在视图类声明serach_fields对特定字段搜索?search=xxx
search_fields = ['^f1']
字段前置支持特殊字符匹配
^:以指定内容开始
=:全匹配
@:全文搜索(当前只支持postgreSql驱动)
$:正则搜索
OrderingFilter:排序过滤器(通过声明ordering_fields对特定或者多个字段排序?ordering=-a,b 字段可多个用英文逗号隔开 符号表示降序)
ordering_fields = []
原生自定义过滤器: 1.重写GenericAPIView类的filter_queryset()方法 2.继承BaseFilterBackend
django-filter扩展衍生的类-
DjangoFilterBackend
自定义: 1.继承FilterSet
class Test(FilterSet):
stu_name = filters.CharFilter(field_name='stu_name', lookup_expr='icontains')
class Meta:
model = StuInfo
fields = ['字段1']
ps: 自定义FilterSet中字段属性相关参数
field_name: 过滤字段名 对应模型类字段名
lookup_expr: 指定ORM查询运算符 lt|gt|gte等等
# 3. 使用
3.1 原生
3.1.1 可全局配置或者局部配置
3.1.2 局部配置在视图类指定filter_backends属性
3.1.3 全局配置在settings.py配置
REST_FRAMEWORK = {
'DEFAULT_FILTER_BACKENDS':[]
}
3.1.4 api访问
?search=xxx
3.2 django-filter扩展
3.2.1 settings.py
INSTALLED_APPS = [
'django_filters', # 注册应用
]
REST_FRAMEWORK = {
'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend',] # 配置过滤器类
}
3.2.2 视图类
class TestViewSet(ListApiView):
filter_fields=('字段1', '字段2')
3.2.3 api访问
?字段名=xxx
# ps: 需要注意django和drf框架的版本
pip install django==3.1 django-filter==2.4.0 djangorestframework==3.12
class StuInfoListViewV2 (GenericAPIView, ListModelMixin):
'''原生SearchFilter'''
serializer_class = StuInfoSerializer
queryset = StuInfo.objects.all ()
filter_backends = [SearchFilter]
search_fields = ['stu_name' , ]
def get (self, request ):
"""
获取所有的学生信息数据
:param request:
:return:
"""
return self.list (request)
class StuInfoListViewV3 (GenericAPIView, ListModelMixin):
'''django-filter'''
serializer_class = StuInfoSerializer
queryset = StuInfo.objects.all ()
filter_backends = [DjangoFilterBackend]
filter_fields = ['stu_scores' , ]
def get (self, request ):
"""
获取所有的学生信息数据
:param request:
:return:
"""
return self.list (request)
from django.conf.urls import url
from . import MixinViews
urlpatterns = [
url(r'^stu2$' , MixinViews.StuInfoListViewV2.as_view()),
url(r'^stu3$' , MixinViews.StuInfoListViewV3.as_view()),
]
4.6 排序
使用
1. 视图类指定属性filter_backends=[OrderingFilter,] 用于指定相关类
2. 视图类指定属性ordering_fields 用于排序字段
3. ?ordering=-xxx,yyy
4. ps:
相关类
rest_framework.filters.OrderingFilter
class StuInfoListViewV4 (GenericAPIView, ListModelMixin):
'''排序'''
serializer_class = StuInfoSerializer
queryset = StuInfo.objects.all ()
filter_backends = [OrderingFilter]
ordering_fields = ['stu_scores' ]
def get (self, request ):
"""
获取所有的学生信息数据
:param request:
:return:
"""
return self.list (request)
from django.conf.urls import url
from . import MixinViews
urlpatterns = [
url(r'^stu4$' , MixinViews.StuInfoListViewV4.as_view()),
]
4.6 分页
1. 分页器类
PageNumberPagination
LimitOffsetPagination
CursorPagination
自定义分页器类(可继承以上三种类或者其父类BasePagination)
2. 使用
全局配置
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS' : 'rest_framework.pagination.PageNumberPagination' ,
'PAGE_SIZE' : 5 ,
}
局部配置
视图类(继承GenericAPIView或其子类)指定pagination_class属性(局部配置优先级高于全局配置)
from django.conf.urls import url
from rest_framework.routers import DefaultRouter, SimpleRouter
from . import myViewSet
urlpatterns = []
router = SimpleRouter()
router.register(prefix='stu1' , viewset=myViewSet.StuInfoViewSetV1, basename='stu1' )
router.register(prefix='stu2' , viewset=myViewSet.StuInfoViewSetV2, basename='stu2' )
router.register(prefix='stu3' , viewset=myViewSet.StuInfoViewSetV3, basename='stu3' )
urlpatterns += router.urls
class MyPageNumberPagination (PageNumberPagination ):
"""从page页展示page_size数据量"""
page_size = 1
page_size_query_param = 'page_size'
page_query_param = 'page'
max_page_size = None
def get_paginated_response (self, data ):
return Response(OrderedDict([
('count' , self.page.paginator.count),
('data' , data)
]))
class MyLimitOffsetPagination (LimitOffsetPagination ):
"""偏移offset 展示limit条数据"""
default_limit = 1
limit_query_param = 'limit'
offset_query_param = 'offset'
max_limit = None
def get_paginated_response (self, data ):
return Response(OrderedDict([
('count' , self.count),
('data' , data)
]))
class MyCursorPagination (CursorPagination ):
"""游标分页只支持展示邻近的上一页或下一页数据 不支持跳转到特定页"""
cursor_query_param = 'cursor'
page_size = 1
ordering = 'id'
page_size_query_param = 'cursor_size'
max_page_size = 2
offset_cutoff = 1000
class StuInfoViewSetV1 (ModelViewSet ):
serializer_class = StuInfoSerializer
queryset = StuInfo.objects.all ()
pagination_class = MyPageNumberPagination
class StuInfoViewSetV2 (ModelViewSet ):
serializer_class = StuInfoSerializer
queryset = StuInfo.objects.all ()
pagination_class = MyLimitOffsetPagination
class StuInfoViewSetV3 (ModelViewSet ):
serializer_class = StuInfoSerializer
queryset = StuInfo.objects.all ()
pagination_class = MyCursorPagination
4.7 异常
1. 异常类
默认:rest_framework.views.exception_handler
自定义:
2. 异常类处理的范围
APIException 所有异常的父类
ParseError 解析异常
AuthenticationFailed 认证失败
NotAuthenticated 未认证
PermissionDenied 没有权限
NotFound 未找到资源
MethodNotAllowed 请求方式不允许
NotAcceptable 获取的数据格式不支持
Throttled 超过限流次数
ValidationError 校验失败
Http404 资源不存在
3. 使用
REST_FRAMEWORK = {
'EXCEPTION_HANDLER' : 'rest_framework.views.exception_handler'
}
from rest_framework.response import Response
from rest_framework.views import exception_handler
def _exception_handler (exc, context ):
'''自定义异常'''
data = {"code" : -1 , "msg" : repr (exc)}
response = Response(data=data, status=500 )
return response
REST_FRAMEWORK = {
'EXCEPTION_HANDLER' : 'utils.myException._exception_handler'
}
class StuInfoListViewV1 (APIView ):
def get (self, request ):
"""
获取所有学生信息
:param request:
:return:
"""
raise ZeroDivisionError('division by zero' )
from django.conf.urls import url
from . import apiViews
urlpatterns = [
url(r'^stu$' , apiViews.StuInfoListViewV1.as_view()),
]
5 帮助文档
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· 什么是nginx的强缓存和协商缓存
· 一文读懂知识蒸馏
· Manus爆火,是硬核还是营销?