Django DRF
详细内容看下面链接
https://www.cnblogs.com/nancyfeng/p/13603069.html
以下内容纯属个人编写
drf 序列化器
什么是序列化与反序列化
"""
序列化:对象转换为字符串用于传输
反序列化:字符串转换为对象用于使用
"""
drf序列化与反序列化
"""
序列化:Model类对象转换为字符串用于传输
反序列化:字符串转换为Model类对象用于使用
"""
创建models类
from django.db import models
# Create your models here.
class Student(models.Model):
name = models.CharField(max_length=32, verbose_name="姓名")
gender = models.BooleanField(default=1, verbose_name="性别")
age = models.IntegerField(verbose_name="年龄")
classNum = models.CharField(max_length=10, verbose_name="班级")
description = models.CharField(max_length=32, verbose_name="个性签名")
class Meta:
db_table = "tb_student"
verbose_name = "学生表"
verbose_name_plural = "学生表"
正常显示页面
就是注册到 views 用类视图
然后巴拉巴拉一大堆 麻烦的要死
但用rest_framework则是
先在项目中新建有个serializers.py的文件
from rest_framework import serializers
# 创建一个序列器类 方便后面视图调用
from students.models import Student
class StuApiModelSerializers(serializers.ModelSerializer):
class Meta:
model = Student
fields = "__all__"
# 这里表明的是字段
# fields = ["id", "name"]
然后再在views.py 中设置
from rest_framework.viewsets import ModelViewSet
# Create your views here.
from students.models import Student
from .serializers import StuApiModelSerializers
class StuApiViewSet(ModelViewSet):
queryset = Student.objects.all()
serializer_class = StuApiModelSerializers
最后在url中注册
from rest_framework.routers import DefaultRouter
from .views import StuApiViewSet
router = DefaultRouter() # 可处理路由的路由器
router.register(prefix="stu", viewset=StuApiViewSet, basename="stu")
# 路由列表
urlpatterns = [
] + router.urls
而序列化则是
serilizers.py
from rest_framework import serializers
"""
serializers 是drf给开发者调用的序列化模块
里面声明了所有的可用序列化的基类
serializer 序列化基类 drf 所有的 许了话器类都必须继承于serializer
ModelSerializer 模型下序列化器类,是序列化基类的子类,在工作中,除了serializer基类意外 最常用的序列化器类基类
"""
class StuSersApiSerializer(serializers.Serializer):
# 只读属性 在反序列化客户端 不会要求你提交此字段
id = serializers.IntegerField(read_only=True)
# 必填字段
name = serializers.CharField(required=True)
# 默认是男性 提交的时候
gender = serializers.BooleanField(default=True)
# 最大值 和最小值
age = serializers.IntegerField(max_value=100,min_value=0,error_messages={
"max_value":"必须小于100",
"min_value":"必须大于0",
})
# 允许为空
classNum = serializers.CharField(allow_null=True,allow_blank=True)
description = serializers.CharField(allow_blank=True,allow_null=True)
views.py
import json
from django.http import JsonResponse
from django.shortcuts import render
from django.views import View
# Create your views here.
from .serializers import StuSersApiSerializer
from students.models import Student
class StuSeriApiView(View):
def get2(self, request):
stu = Student.objects.all()
# 1. 序列化一个对象 得到序列化对象
ser = StuSersApiSerializer(instance=stu, many=True)
# 2. 调用序列化的对象的data 得到数据
data = ser.data
# 3. 响应数据
return JsonResponse(data=data, safe=False, status=200)
def get(self, request):
# 1. 接受客户端发送来的信息
# data = json.dumps(request.body)
data = {
"name": "xh",
"gender": False,
"age": -17,
"classNum": "301",
"description": "hello",
}
# 1.1 实例化序列化器 获取序列化对象
serializer=StuSersApiSerializer(data=data)
# 1.2 调用序列化器 进行数据验证
ret =serializer.is_valid()
print(f"{ret}")
# 获取结果后 进行操作
if ret:
return JsonResponse(dict(serializer.validated_data))
else:
return JsonResponse(dict(serializer.errors))
# 2. 操作数据库
# 3. 响应数据
urls.py
from django.conf.urls import re_path
from django.urls import path
from .views import *
urlpatterns=[
path("seri1/",StuSeriApiView.as_view())
]
数据校验的方法
多个字段进行验证
而在序列化器中 需要对多个字段进行验证 所以需要 用到validate
seralizers.py中
from rest_framework import serializers
"""
serializers 是drf给开发者调用的序列化模块
里面声明了所有的可用序列化的基类
serializer 序列化基类 drf 所有的 许了话器类都必须继承于serializer
ModelSerializer 模型下序列化器类,是序列化基类的子类,在工作中,除了serializer基类意外 最常用的序列化器类基类
"""
class StuSersApiSerializer(serializers.Serializer):
# 只读属性 在反序列化客户端 不会要求你提交此字段
id = serializers.IntegerField(read_only=True)
# 必填字段
name = serializers.CharField(required=True)
# 默认是男性 提交的时候
gender = serializers.BooleanField(default=True)
# 最大值 和最小值
age = serializers.IntegerField(max_value=100, min_value=0, error_messages={
"max_value": "必须小于100",
"min_value": "必须大于0",
})
# 允许为空
classNum = serializers.CharField(allow_null=True, allow_blank=True)
description = serializers.CharField(allow_blank=True, allow_null=True)
# validate 这个字段是对所有字段进行验证的
def validate(self, attrs):
"""
验证客户端的所有数据
类所以于会员注册的密码和确认密码 就只能在validate的方法中校对
validate是固定字段名
而attr是实例化器实例化时的data数据选项
"""
# 301 只能有女生 不能加入其他男生
if attrs["classNum"] == "301" and attrs["gender"]:
raise serializers.ValidationError(detail="301是女生班级 不可以进")
return attrs
视图代码views.py
def get(self, request):
# 1. 接受客户端发送来的信息
# data = json.dumps(request.body)
data = {
"name": "xh",
"gender": False,
"age": 17,
"classNum": "301",
"description": "这家伙很懒 什么都没有留下",
}
# 1.1 实例化序列化器 获取序列化对象
serializer = StuSersApiSerializer(data=data)
# 1.2 调用序列化器 进行数据验证
serializer.is_valid(raise_exception=True)
print(data)
# 2. 操作数据库
# 3. 响应数据
return JsonResponse({})
效果图
单字段进行验证
写在 serializers.py
中
validate_<字段名>
class StuSersApiSerializer(serializers.Serializer):
# 只读属性 在反序列化客户端 不会要求你提交此字段
id = serializers.IntegerField(read_only=True)
# 必填字段
name = serializers.CharField(required=True)
# 默认是男性 提交的时候
gender = serializers.BooleanField(default=True)
# 最大值 和最小值
age = serializers.IntegerField(max_value=100, min_value=0, error_messages={
"max_value": "必须小于100",
"min_value": "必须大于0",
})
# 允许为空
classNum = serializers.CharField(allow_null=True, allow_blank=True)
description = serializers.CharField(allow_blank=True, allow_null=True)
# validate 这个字段是对所有字段进行验证的
def validate(self, attrs):
"""
验证客户端的所有数据
类所以于会员注册的密码和确认密码 就只能在validate的方法中校对
validate是固定字段名
而attr是实例化器实例化时的data数据选项
"""
# 301 只能有女生 不能加入其他男生
if attrs["classNum"] == "301" and attrs["gender"]:
raise serializers.ValidationError(detail="301是女生班级 不可以进")
return attrs
def validate_name(self, data):
"""
验证单个字段
方法名的格式必须v以validate_<字段名> 为名词 否则 序列化识别不到!
validate 开头的方法 户自动被 is_valid调用
"""
if data in ["python", "django"]:
# 在序列化中 验证失败可以通过抛出异常 的方式 来告知 is valid
raise serializers.ValidationError(detail="学生名不能是python 或者 django ", code="validate_name")
# 验证完数据后 必须v返回数据 否则最终的验证结果中 就不会出现该数据
return data
#!!!!!!!!!!!return data 一定要返回不然这个字段就是None
views.py
def get(self, request):
# 1. 接受客户端发送来的信息
# data = json.dumps(request.body)
data = {
"name": "python",
"gender": False,
"age": 17,
"classNum": "301",
"description": "这家伙很懒 什么都没有留下",
}
# 1.1 实例化序列化器 获取序列化对象
serializer = StuSersApiSerializer(data=data)
# 1.2 调用序列化器 进行数据验证
serializer.is_valid(raise_exception=True)
print(data)
# 2. 操作数据库
# 3. 响应数据
return JsonResponse({})
效果图
外部验证函数
def check_classnum(data):
"""外部验证函数"""
if len(data) != 3:
raise serializers.ValidationError(detail="格式不正确 必须v是三位数", code="check_classnum")
# 验证完毕 必须返回 数据 不然 数据等于None 验证结果中没有该数据
return data
class StuSersApiSerializer(serializers.Serializer):
# 只读属性 在反序列化客户端 不会要求你提交此字段
id = serializers.IntegerField(read_only=True)
# 必填字段
name = serializers.CharField(required=True)
# 默认是男性 提交的时候
gender = serializers.BooleanField(default=True)
# 最大值 和最小值
age = serializers.IntegerField(max_value=100, min_value=0, error_messages={
"max_value": "必须小于100",
"min_value": "必须大于0",
})
# 允许为空 单独指定一个方法 validators 是外部函数验证选项 嵌套列表 列表的字段是函数 不能是字符串
classNum = serializers.CharField(validators=[check_classnum])
description = serializers.CharField(allow_blank=True, allow_null=True)
效果如下
反序列化 增加和更新操作
增加
seralizers.py
class StuSeriApiView(View):
# 创建 添加
def get(self, request):
# 1. 接受客户端发送来的信息
# data = json.dumps(request.body)
data = {
"name": "pyth1on",
"gender": False,
"age": 17,
"classNum": "311",
"description": "这家伙很懒 什么都没有留下",
}
# 1.1 实例化序列化器 获取序列化对象
serializer = StuSersApiSerializer(data=data)
# 1.2 调用序列化器 进行数据验证
serializer.is_valid(raise_exception=True)
"""
# # 2. 操作数据库
# student = Student.objects.create(**serializer.data)
# # print("student:",student) # 是一个 object 对象
# serializer = StuSersApiSerializer(instance=student)
# # print("serializer:",serializer) # 是 serializer
"""
# 2. 获取验证以后的结果 操作数据
serializer.save() # 会根据实例化徐猎奇的时候 是否传入 instance属性来自动调用create方法
# # 3. 响应数据
return JsonResponse(serializer.data, status=201)
serializers.py
class StuSersApiSerializer(serializers.Serializer):
# 只读属性 在反序列化客户端 不会要求你提交此字段
id = serializers.IntegerField(read_only=True)
# 必填字段
name = serializers.CharField(required=True)
# 默认是男性 提交的时候
gender = serializers.BooleanField(default=True)
# 最大值 和最小值
age = serializers.IntegerField(max_value=100, min_value=0, error_messages={
"max_value": "必须小于100",
"min_value": "必须大于0",
})
# 允许为空 单独指定一个方法 validators 是外部函数验证选项 嵌套列表 列表的字段是函数 不能是字符串
classNum = serializers.CharField(validators=[check_classnum])
description = serializers.CharField(allow_blank=True, allow_null=True)
.................
.................
def create(self, validated_data):
"""
添加数据
添加数据操作 就自动实现了从字典变成模型对象的过程
方法名固定为 create
固定参数 为: validated_data 就是验证成功后的结果
"""
student = Student.objects.create(**validated_data)
return student
更新
views.py
# 更新操作
def get(self, request):
# 根据客户端访问的url 获得pk值
# sers/student/2 path("/student/?<pk>\d+/")
pk = 3
try:
student = Student.objects.get(id=pk)
except Student.DoesNotExist:
return JsonResponse({"error": "该学生不存在"}, status=400)
# 2. 接受客户端提供的数据
data = {
"name": "xiaohon111g",
"age": 18,
"gender": False,
"classNum": "301",
"description": "这家伙很懒 什么都没有留下"
}
# 3. 修改操作中的实例化序列器对象
serializer = StuSersApiSerializer(instance=student, data=data)
# 4.验证数据
serializer.is_valid(raise_exception=True)
# 5. 入库
serializer.save() # 可以在save 中 传递一些不需要验证的数据到模型里面
# 6. 返回结果
return JsonResponse(serializer.data, status=200)
serializers.py
def update(self, instance, validated_data):
"""
更新数据操作
更新数据操作 就自动实现了从字典变成模型对象的过程
方法名固定为:update
固定参数 instance 实现序列化对象时,必须v传入的模型对象
固定参数 validated_data 就是严惩成功后的结果
"""
# # todo: 太麻烦 直接用 for 循环 kv 写入
# instance.name = validated_data["name"]
# instance.age = validated_data["age"]
# instance.gender = validated_data["gender"]
# instance.classNum = validated_data["classNum"]
# instance.description = validated_data["description"]
# # 调用模型对象的save方法 和views视图中的serializers.save 不是一个方法
for k, v in validated_data.items():
setattr(instance, k, v)
instance.save() # 调用模型对象中的save方法 和 视图中的serializers的save 不是一个方法
return instance
附加说明
在序列化操作进行save()保存时 可以额外传递数据 这些数据可以在create 和update的validdated_date参数 获取到
# request.user 是Django中 记录当前用户的模型对象
serializer.save(owner=request.user) # 可以在save中 传递一些不需要的验证的数据到模型里面
默认序列化器 必须传递 所有的require的字段 否则会抛出验证异常,但是完美可以使用partial参数来允许部分字段更新
# 更新`name` 不需要验证 其他的字段 可以设置 partial=True
serializer = StudentApiSerializer(student,data={"name":"xiaoming"},partial=True)
模型序列化器 的操作和使用
models.py
from django.db import models
# Create your models here.
class Student(models.Model):
name = models.CharField(max_length=32, verbose_name="姓名")
gender = models.BooleanField(default=1, verbose_name="性别")
age = models.IntegerField(verbose_name="年龄")
classNum = models.CharField(max_length=10, verbose_name="班级")
description = models.CharField(max_length=32, verbose_name="个性签名")
class Meta:
db_table = "tb_student"
verbose_name = "学生表"
verbose_name_plural = "学生表"
serializers.py
class StudentModelSerializer(serializers.ModelSerializer):
# 1. 转换的字段声明
# 字段名 = 选项类型(选项=选项值)
# 可以手动探究啊自定义字段
nickname=serializers.CharField(read_only=True)
# 2. 如果当前序列化继承的是 modelserializer 则需要声明调用的模型类型
class Meta:
model = Student
# 这里表明的是字段
fields = ["id", "name","nickname"]
# fields ="__all__" # 字符串只能是这个 代表所有字段
# 表示的是只读字段 表示设置的字段 只会在序列化阶段显示
# read_only_fields=[]
extract_kwargs = {
# 选填 字段额外生命
# 字段名:{
# "选项名":"选项值"
# }
}
# 3. 验证代码的对象方法
# 4. 模型操作的方法
views.py
class StudentView(View):
def get1(self, request):
# 1. 序列化一个对象 得到序列化对象
student = Student.objects.first()
#
student.nickname="小学生"
serializer = StudentModelSerializer(student)
# 2. 调用序列化的对象的data 得到数据
data = serializer.data
# 3. 相应数据
return JsonResponse(data=data, safe=False, status=200,json_dumps_params={"ensure_ascii":False})
def get2(self, request):
stu = Student.objects.all()
# 1. 序列化一个对象 得到序列化对象
ser = StudentModelSerializer(instance=stu, many=True)
# 2. 调用序列化的对象的data 得到数据
data = ser.data
# 3. 响应数据
return JsonResponse(data=data, safe=False, status=200)
def get3(self, request):
# 1. 接受客户端发送来的信息
# data = json.dumps(request.body)
data = {
"name": "xh",
"gender": True,
"age": 117,
"classNum": "301",
"description": "hello",
}
# 1.1 实例化序列化器 获取序列化对象
serializer = StudentModelSerializer(data=data)
# 1.2 调用序列化器 进行数据验证
ret = serializer.is_valid()
print(f"{ret}")
# 获取结果后 进行操作
if ret:
return JsonResponse(dict(serializer.validated_data))
else:
return JsonResponse(dict(serializer.errors))
# 2. 操作数据库
# 3. 响应数据
def get4(self, request):
# 1. 接受客户端发送来的信息
# data = json.dumps(request.body)
data = {
"name": "pyth1on",
"gender": False,
"age": 117,
"classNum": "3011",
"description": "这家伙很懒 什么都没有留下",
}
# 1.1 实例化序列化器 获取序列化对象
serializer = StudentModelSerializer(data=data)
# 1.2 调用序列化器 进行数据验证
serializer.is_valid(raise_exception=True)
print(data)
# 2. 操作数据库
# 3. 响应数据
return JsonResponse({})
# 创建 添加
def get(self, request):
# 1. 接受客户端发送来的信息
# data = json.dumps(request.body)
data = {
"name": "pyth1on",
"gender": False,
"age": 17,
"classNum": "311",
"description": "这家伙很懒 什么都没有留下",
}
# 1.1 实例化序列化器 获取序列化对象
serializer = StudentModelSerializer(data=data)
# 1.2 调用序列化器 进行数据验证
serializer.is_valid(raise_exception=True)
"""
# # 2. 操作数据库
# student = Student.objects.create(**serializer.data)
# # print("student:",student) # 是一个 object 对象
# serializer = StuSersApiSerializer(instance=student)
# # print("serializer:",serializer) # 是 serializer
"""
# 2. 获取验证以后的结果 操作数据
serializer.save() # 会根据实例化徐猎奇的时候 是否传入 instance属性来自动调用create方法
# # 3. 响应数据
return JsonResponse(serializer.data, status=201)
# 更新操作
def get6(self, request):
# 根据客户端访问的url 获得pk值
# sers/student/2 path("/student/?<pk>\d+/")
pk = 3
try:
student = Student.objects.get(id=pk)
except Student.DoesNotExist:
return JsonResponse({"error": "该学生不存在"}, status=400)
# 2. 接受客户端提供的数据
data = {
"name": "xiaohong",
"age": 18,
"gender": False,
"classNum": "301",
"description": "这家伙很懒 什么都没有留下"
}
# 3. 修改操作中的实例化序列器对象
serializer = StudentModelSerializer(instance=student, data=data)
# 4.验证数据
serializer.is_valid(raise_exception=True)
# 5. 入库
serializer.save() # 可以在save 中 传递一些不需要验证的数据到模型里面
# 6. 返回结果
return JsonResponse(serializer.data, status=201)
drf 中视图调用的http请求和响应处理类
drf除了在数据序列化部分简写代码以外 还在视图中提供了简写操作 ,所以在django原有的django views,views.views类基础上
drf 封装了多个做了出来提供给我们使用。
Django DRF framwork提供视图的主要作用:
- 数据序列化的执行(检验,保存,转换数据)
- 控制数据库查询的执行
- 调用请求类和响应类,这两个了也是由drf 帮我们再次扩展了一些功能类)
views.py
from django.http import HttpResponse
from django.views import View
from rest_framework.views import APIView
from rest_framework.response import Response # drf的response 是django HttpResponse对象的子类
from rest_framework import status # 保存了 所有http响应状态对应的代码
# Create your views here.
class StudentView(View):
def get(self, request):
print(fr"request={request}") # wsgi httpresonse 在视图中传递的变量是wsgi
return HttpResponse("ok")
class StudentApiView(APIView):
def get(self, request):
print(f"request={request}") # 和 上面的request 没有关系
# 自定义响应头
return Response({"msg": "ok"}, status=status.HTTP_201_CREATED, headers={"company": "i want to money"})
def post(self, request):
"""
添加数据/获取请求体数据
"""
print(f"request.data={request.data}") # 接受的数据 已经被Parser解析器转换成字典数据了
print(request.data.get("name"))
"""
获取查询参数/ 查询字符串
"""
# query_params 和原生Django 的request.get 相同 只是更换了名字
print(f"request.query_params={request.query_params}")
return Response({"msg": "ok"})
def put(self, request):
"""
更新数据
"""
print(f"request={request}") # 和 上面的request 没有关系
return Response({"msg": "ok"})
def patch(self, request):
"""
更新数据[部分]
"""
print(f"request={request}") # 和 上面的request 没有关系
return Response({"msg": "ok"})
def delete(self, request):
"""
删除数据
"""
print(f"request={request}") # 和 上面的request 没有关系
return Response({"msg": "ok"})
基本视图类APIView和通用视图类GenericAPIView的声明和使用
APIView基本视图类
from rest_framework.views import APIView
APIView 是 REST framework 提供的 所有视图类的积累 继承自Django 的View 弗雷
APIView
和 View
不同之处在于
-
传入的视图方法是REST framework的Request 对象,而不是Django 的HttpRequest对象:
-
视图方法可以返回REST framework的Response对象,视图会为响应数据设置符合的前端期望要求的格式。
-
任意APIException一场都会被捕获到,并且处理成合格格式的响应信息返回给客户端:
- Django 的View 所有一场全部以HTML格式显示
- DRF中的APIView 的子类会自动根据 客户端中的accept进行的错误格式进行转换
-
重新生命应该新的as_view方法并在 dispatch()进行 路由分发前 会对请求的客户端进行身份验证,权限检查,流量控制。
APIView新增加了类属性
authentication_classes 列表或者元组 身份认证类
permission_classes 列表或者元组 权限检查类
throttle_classes 列表或者元组 流量控制类
在APIView 中仍以常规的类视图定义方法来实现get(),post 或者请求其他方式的方法
序列化器serializer.py
from students.models import Student
from rest_framework import serializers
class StudentModelSerializer(serializers.ModelSerializer):
class Meta:
model = Student
fields = "__all__"
extra_kwargs = {
# 选填 字段额外生命
# 字段名:{
# "选项名":"选项值"
# }
"age": {
"min_value": 5,
"max_value": 20,
"error_messages": {
"min_value": "年龄最小不能小于5",
"max_value": "年龄最大不能大于20",
},
}
}
views.py
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework import status
# Create your views here.
from demo.serializers import StudentModelSerializer
from students.models import Student
class StudentAPIView(APIView):
"""
GET /demo/students/ 获取所有学生信息
POST /demo/students/ 添加一个学生信息
GET /demo/students/<pk> 获取一个学生信息
PUT /demo/students/<pk> 更新一个学生信息
DELETE /demo/students/<pk> 删除一个学生信息
"""
def get(self, request):
"""
获取所有学生信息
"""
# 1. 从数据库中读取学生信息列表
student_list = Student.objects.all()
# 2. 实例化序列化器, 获取序列化对象
serializer = StudentModelSerializer(instance=student_list, many=True)
# 3. 转换 数据并且返回给客户端
return Response(serializer.data, status=status.HTTP_200_OK)
def post(self, request):
"""
添加一条数据
"""
# 1.获取客户端 提交的数据
# request.data # 获取客户端提交的数据
# 2.实例化序列化器,获取序列化器对象
serializer = StudentModelSerializer(data=request.data)
# 3. 反序列化[验证数据,保存数据到数据库]
serializer.is_valid(raise_exception=True)
serializer.save()
# 4. 返回新增的模型数据给客户端
return Response(serializer.data,status=status.HTTP_201_CREATED)
def put(self, request):
pass
def delete(self, request):
pass
class StudentInfoView(APIView):
def get(self,request,pk):
"""
获取一条数据
"""
# 1. 使用pk 作为条件获取模型对象
try:
student=Student.objects.get(pk=pk)
except Student.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
# 2. 序列化
serializer= StudentModelSerializer(instance=student)
# 3. 返回结果
return Response(serializer.data)
def put(self,request,pk):
"""
更新数据
"""
# 1. 使用pk 获取要更新的数据
try:
student=Student.objects.get(pk=pk)
except Student.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
# 2. 获取客户端提交的数据
serializer= StudentModelSerializer(instance=student,data=request.data)
# 3. 反序列化[验证数据和数据保存]
serializer.is_valid(raise_exception=True)
serializer.save()
# 4. 返回结果
return Response(serializer.data)
def delete(self,request,pk):
"""
删除数据
"""
# 1. 使用pk 获取要删除的数据并删除
try:
student=Student.objects.get(pk=pk).delete()
except Student.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
# 2. 结果
return Response(status.HTTP_204_NO_CONTENT)
路由 urls.py
from django.urls import path
from .views import *
urlpatterns = [
path("students/", StudentAPIView.as_view()),
path("students/<int:pk>", StudentInfoView.as_view()),
]
GenericAPIView(通用视图类)
通用视图类的主要作用就是把视图中的独特的代码 抽离出来 使得代码更加通用 方便把代码进行简写
GenericAPIView 继承于 APIView
主要增加了操作序列化和操作数据库的方法 作用是为下面mixin扩展类的执行提供方法支持 通常在使用时,可以搭配应该或者多个Mixin扩展类
views.py
"""GenericApIView通用视图类"""
from rest_framework.generics import GenericAPIView
class StudentGenericAPIView(GenericAPIView):
queryset = Student.objects.all()
serializer_class = StudentModelSerializer
def get(self, request):
"""获取所有数据"""
# 1. 从数据库获取学生列表信息
queryset = self.get_queryset() # Generic APIView提供的get_queryset(
# 2. 序列化
serializer = self.get_serializer(instance=queryset, many=True)
# 3. 转换数据并返回给客户端
return Response(serializer.data)
def post(self, request):
"""添加一个数据"""
# 1. 获取客户端 提交的数据 实现序列化器,获取序列化兑现
serializer = self.get_serializer(data=request.data)
# 2. 反序列化[验证数据,保存数据到数据库]
serializer.is_valid(raise_exception=True)
serializer.save()
# 3. 返回新增的模型数据给客户端
return Response(serializer.data, status=status.HTTP_201_CREATED)
class StudentInfoGenericApiView(GenericAPIView):
queryset = Student.objects.all()
serializer_class = StudentModelSerializer
def get(self, request, pk):
"""获取一个数据"""
# 1.使用pk 作为条件获取数据
instance = self.get_object()
# instance = self.queryset().get(pk=3)
# 2.序列化
serializer = self.get_serializer(instance=instance)
# 3. 返回结果
return Response(serializer.data)
def put(self,request,pk):
"""更新一条数据"""
# 1.使用pk 作为条件获取数据
instance = self.get_object()
# 2. 获取客户端提交的数据
serializer = self.get_serializer(instance=instance,data=request.data)
# 3. 反序列化[验证数据,保存数据到数据库]
serializer.is_valid(raise_exception=True)
serializer.save()
# 4. 返回结果
return Response(serializer.data,status=status.HTTP_201_CREATED)
def delete(self,request,pk):
"""
删除数据
"""
# 1. 使用pk 获取要删除的数据并删除
try:
student = self.get_object().delete()
except Student.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
# 2. 结果
return Response(status.HTTP_204_NO_CONTENT)
url.py
from django.urls import path
from .views import *
urlpatterns = [
# GenericAPIView
path("students2/",StudentGenericAPIView.as_view()),
path("students2/<int:pk>", StudentInfoGenericApiView.as_view()),
]
基于Mixin模型扩展类+GenericView 通用类
views.py
"""
使用drf 内置的模型扩展类[混入类] 结合GenericAPIView 实现通用视图的简写操作
from rest_framework.mixins import ListModelMixin 获取多条数据 返回结果
from rest_framework.mixins import CreateModelMixin 添加一条数据 返回结果
from rest_framework.mixins import RetrieveModelMixin 获取一条数据 返回结果
from rest_framework.mixins import UpdateModelMixin 更新一条数据 返回结果
from rest_framework.mixins import DestroyModelMixin 删除一条数据 返回结果
"""
from rest_framework.mixins import ListModelMixin, CreateModelMixin
class StudentMixinsView(GenericAPIView, ListModelMixin, CreateModelMixin):
queryset = Student.objects.all()
serializer_class = StudentModelSerializer
def get(self, request):
"获取所有数据"
return self.list(request)
def post(self, request):
"添加一条数据"
return self.create(request)
from rest_framework.mixins import RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin
class StudentInfoMixinsView(GenericAPIView,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin):
queryset = Student.objects.all()
serializer_class = StudentModelSerializer
def get(self,request,pk):
"""
获取一条数据
"""
return self.retrieve(request,pk)
def put(self,request,pk):
"""
更新一条数据
"""
return self.update(request,pk)
def delete(self,request,pk):
"""
删除一条数据
"""
return self.destroy(request,pk)
urls.py
# GenericAPIView+Mixin
path("students3/",StudentMixinsView.as_view()),
path("students3/<int:pk>",StudentInfoMixinsView.as_view()),
视图子类
views.py
"""
视图子类是通用试图类,和模型扩展类的子类
上面的接口代码 还可以继续更加的精简drf 在使用genericAPIView和mixins合并后 还提供了视图子类
视图子类 提供了各种视图方法 调用mixins操作
ListAPIView = GenericAPIView + ListModelMixin # 获取多条数据的视图方法
CreateAPIView = GenericAPIView + CreateModelMixin # 添加一条数据的视图方法
RetrieveAPIView = GenericAPIView + RetrieveModelMixin # 获取一条数据的视图方法
UpdateAPIView = GenericAPIView+UpdateModelMixin # 更新一条数据的视图方法
DestroyAPIView = GenericAPIView + DestroyModelMixin # 删除一条数据的视图方法
组合视图子类
ListCreateAPIView = ListAPIView+CreateAPIView
RetrieveUpdateAPIView = RetrieveAPIView+UpdateAPIView
RetrieveDestroyAPIView = RetrieveAPIView+DestroyAPIView
RetrieveUpdateDestroyAPIView=RetrieveAPIView+UpdateAPIView
"""
from rest_framework.generics import ListAPIView, CreateAPIView, RetrieveAPIView, DestroyAPIView, UpdateAPIView
from rest_framework.generics import ListCreateAPIView,RetrieveUpdateDestroyAPIView
# class StudentView(ListAPIView, CreateAPIView):
class StudentView(ListCreateAPIView):
queryset = Student.objects.all()
serializer_class = StudentModelSerializer
# class StudentInfoView(RetrieveAPIView, DestroyAPIView, UpdateAPIView):
class StudentInfoView(RetrieveUpdateDestroyAPIView):
queryset = Student.objects.all()
serializer_class = StudentModelSerializer
url.py
# ListAPIView+CreateAPIView+...
path("students4/",StudentView.as_view()),
path("students4/<int:pk>",StudentInfoMixinsView.as_view()),
基于视图集和路由集实现API接口
视图集ViewSet
view.py
"""
在上面的接口实现 过程中 也存在代码重复的情况,如果我们合并成一个接口类,则需要考虑两个问题
1. 路由的合并问题
2. get方法重复的问题
drf提供了视图集来解决这个问题
"""
"""
ViewSet -> APIView的代码重复问题 基本视图集
"""
from rest_framework.viewsets import ViewSet
class StudentViewSet(ViewSet):
def get_list(self, request):
"""获取所有数据"""
# 1. 从数据库获取学生列表信息
student_list = Student.objects.all()
# 2. 序列化
serializer = StudentModelSerializer(instance=student_list, many=True)
# 3. 转换数据并返回给客户端
return Response(serializer.data)
def post(self, request):
"""
添加一条数据
"""
# 1.获取客户端 提交的数据
# request.data # 获取客户端提交的数据
# 2.实例化序列化器,获取序列化器对象
serializer = StudentModelSerializer(data=request.data)
# 3. 反序列化[验证数据,保存数据到数据库]
serializer.is_valid(raise_exception=True)
serializer.save()
# 4. 返回新增的模型数据给客户端
return Response(serializer.data, status=status.HTTP_201_CREATED)
def get_student_info(self, request, pk):
"""
获取一条数据
"""
# 1. 使用pk 作为条件获取模型对象
try:
student = Student.objects.get(pk=pk)
except Student.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
# 2. 序列化
serializer = StudentModelSerializer(instance=student)
# 3. 返回结果
return Response(serializer.data)
def update_student_info(self, request, pk):
"""
更新数据
"""
# 1. 使用pk 获取要更新的数据
try:
student = Student.objects.get(pk=pk)
except Student.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
# 2. 获取客户端提交的数据
serializer = StudentModelSerializer(instance=student, data=request.data)
# 3. 反序列化[验证数据和数据保存]
serializer.is_valid(raise_exception=True)
serializer.save()
# 4. 返回结果
return Response(serializer.data)
def delete_student_info(self, request, pk):
"""
删除数据
"""
# 1. 使用pk 获取要删除的数据并删除
try:
student = Student.objects.get(pk=pk).delete()
except Student.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
# 2. 结果
return Response(status.HTTP_204_NO_CONTENT)
路由
urls.py
from django.urls import path
from .views import *
urlpatterns = [
# APIView
path("students/", StudentAPIView.as_view()),
path("students/<int:pk>", StudentAPIInfoView.as_view()),
# GenericAPIView
path("students2/", StudentGenericAPIView.as_view()),
path("students2/<int:pk>", StudentInfoGenericApiView.as_view()),
# GenericAPIView+Mixin
path("students3/", StudentMixinsView.as_view()),
path("students3/<int:pk>", StudentInfoMixinsView.as_view()),
# ListAPIView+CreateAPIView+...
path("students4/", StudentView.as_view()),
path("students4/<int:pk>", StudentInfoMixinsView.as_view()),
# 视图集 ViewSet
path("students5/", StudentViewSet.as_view(
{
"get": "get_list", # 视图类方法 可以是原来的http请求 也可以是自己自定义的方法名
"post": "post",
}
)),
path("students5/<int:pk>", StudentViewSet.as_view(
{
"get": "get_student_info",
"put": "update_student_info",
"delete": "delete_student_info",
}
)),
]
通用视图集GenericViewSet
views.py
"""
GenericViewSet APIView的代码重复问题 通用视图集 同时使得代码更加通用
"""
from rest_framework.viewsets import GenericViewSet
class StudentGenericViewSet(GenericViewSet):
queryset = Student.objects.all()
serializer_class = StudentModelSerializer
def list(self, request):
"""获取所有数据"""
# 1. 从数据库获取学生列表信息
queryset = self.get_queryset() # Generic APIView提供的get_queryset(
# 2. 序列化
serializer = self.get_serializer(instance=queryset, many=True)
# 3. 转换数据并返回给客户端
return Response(serializer.data)
def create(self, request):
"""添加一个数据"""
# 1. 获取客户端 提交的数据 实现序列化器,获取序列化兑现
serializer = self.get_serializer(data=request.data)
# 2. 反序列化[验证数据,保存数据到数据库]
serializer.is_valid(raise_exception=True)
serializer.save()
# 3. 返回新增的模型数据给客户端
return Response(serializer.data, status=status.HTTP_201_CREATED)
def retrieve(self, request, pk):
"""获取一个数据"""
# 1.使用pk 作为条件获取数据
instance = self.get_object()
# instance = self.queryset().get(pk=3)
# 2.序列化
serializer = self.get_serializer(instance=instance)
# 3. 返回结果
return Response(serializer.data)
def update(self, request, pk):
"""更新一条数据"""
# 1.使用pk 作为条件获取数据
instance = self.get_object()
# 2. 获取客户端提交的数据
serializer = self.get_serializer(instance=instance, data=request.data)
# 3. 反序列化[验证数据,保存数据到数据库]
serializer.is_valid(raise_exception=True)
serializer.save()
# 4. 返回结果
return Response(serializer.data, status=status.HTTP_201_CREATED)
def destroy(self, request, pk):
"""
删除数据
"""
# 1. 使用pk 获取要删除的数据并删除
try:
student = self.get_object().delete()
except Student.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
# 2. 结果
return Response(status.HTTP_204_NO_CONTENT)
urls.py
from django.urls import path
from .views import *
urlpatterns = [
# APIView
path("students/", StudentAPIView.as_view()),
path("students/<int:pk>", StudentAPIInfoView.as_view()),
# GenericAPIView
path("students2/", StudentGenericAPIView.as_view()),
path("students2/<int:pk>", StudentInfoGenericApiView.as_view()),
# GenericAPIView+Mixin
path("students3/", StudentMixinsView.as_view()),
path("students3/<int:pk>", StudentInfoMixinsView.as_view()),
# ListAPIView+CreateAPIView+...
path("students4/", StudentView.as_view()),
path("students4/<int:pk>", StudentInfoMixinsView.as_view()),
# 视图集 ViewSet
path("students5/", StudentViewSet.as_view(
{
"get": "get_list", # 视图类方法 可以是原来的http请求 也可以是自己自定义的方法名
"post": "post",
}
)),
path("students5/<int:pk>", StudentViewSet.as_view(
{
"get": "get_student_info",
"put": "update_student_info",
"delete": "delete_student_info",
}
)),
# 通用视图集 GenericViewSet
path("students6/", StudentGenericViewSet.as_view(
{
"get": "list", # 视图类方法 可以是原来的http请求 也可以是自己自定义的方法名
"post": "create",
}
)),
path("students6/<int:pk>", StudentGenericViewSet.as_view(
{
"get": "retrieve",
"put": "update",
"delete": "destroy",
}
)),
]
通用视图集+mixin混合类
views.py
"""
GenericViewSet 通用视图集+mixin混合类
"""
# 继承的五个类 刚好有 五个方法 list create update retrieve destroy
class StudentGenericMixinViewSet(GenericViewSet, ListModelMixin, CreateModelMixin, UpdateModelMixin, RetrieveModelMixin,
DestroyModelMixin):
queryset = Student.objects.all()
serializer_class = StudentModelSerializer
"""
上面的接口类继承的父类太多了
我们可以用一些合并的视图父类让其继承
ReadOnlyModelViewSet = mixins.RetrieveModelMixin + mixins.listModelMixin+GenericView
获取多条数据
获取一条数据
"""
# modelViewSet 实现了五合一
from rest_framework.viewsets import ReadOnlyModelViewSet
class StudentReadonlyMixinViewSet(ReadOnlyModelViewSet, CreateModelMixin, UpdateModelMixin, DestroyModelMixin):
queryset = Student.objects.all()
serializer_class = StudentModelSerializer
from rest_framework.viewsets import ModelViewSet
class StudentModelViewSet(ModelViewSet):
queryset = Student.objects.all()
serializer_class = StudentModelSerializer
urls.py
from django.urls import path
from .views import *
urlpatterns = [
# APIView
path("students/", StudentAPIView.as_view()),
path("students/<int:pk>", StudentAPIInfoView.as_view()),
# GenericAPIView
path("students2/", StudentGenericAPIView.as_view()),
path("students2/<int:pk>", StudentInfoGenericApiView.as_view()),
# GenericAPIView+Mixin
path("students3/", StudentMixinsView.as_view()),
path("students3/<int:pk>", StudentInfoMixinsView.as_view()),
# ListAPIView+CreateAPIView+...
path("students4/", StudentView.as_view()),
path("students4/<int:pk>", StudentInfoMixinsView.as_view()),
# 视图集 ViewSet
path("students5/", StudentViewSet.as_view(
{
"get": "get_list", # 视图类方法 可以是原来的http请求 也可以是自己自定义的方法名
"post": "post",
}
)),
path("students5/<int:pk>", StudentViewSet.as_view(
{
"get": "get_student_info",
"put": "update_student_info",
"delete": "delete_student_info",
}
)),
# 通用视图集 GenericViewSet
path("students6/", StudentGenericViewSet.as_view(
{
"get": "list", # 视图类方法 可以是原来的http请求 也可以是自己自定义的方法名
"post": "create",
}
)),
path("students6/<int:pk>", StudentGenericViewSet.as_view(
{
"get": "retrieve",
"put": "update",
"delete": "destroy",
}
)),
# 通用视图集 GenericViewSet+mixin 混合类
path("students6/", StudentReadonlyMixinViewSet.as_view(
{
"get": "list", # 视图类方法 可以是原来的http请求 也可以是自己自定义的方法名
"post": "create",
}
)),
path("students6/<int:pk>", StudentGenericViewSet.as_view(
{
"get": "retrieve",
"put": "update",
"delete": "destroy",
}
)),
# 通用视图集 ReadonlyViewSet
path("students7/", StudentReadonlyMixinViewSet.as_view(
{
"get": "list", # 视图类方法 可以是原来的http请求 也可以是自己自定义的方法名
"post": "create",
}
)),
path("students7/<int:pk>", StudentReadonlyMixinViewSet.as_view(
{
"get": "retrieve",
"put": "update",
"delete": "destroy",
}
)),
]
路由Routers
无形之中 我们的路由就增加了 所以需要简化代码
urls.py
# 自动生成路由信息(和视图集一起使用)
from rest_framework.routers import SimpleRouter, DefaultRouter
# 1. 实例化一个路由器
router = DefaultRouter()
# router = SimpleRouter() # 会缺失API主界面
# 2. 给路由注册去注册视图集
router.register("students9", StudentModelViewSet, basename="students9")
router.register("students10", StudentModelViewSet, basename="students10")
print(router.urls)
# 3. 把生成的路由列表和原路由进行拼接
urlpatterns += router.urls
用action 封装 路由
class StudentModelViewSet(ModelViewSet):
queryset = Student.objects.all()
serializer_class = StudentModelSerializer
# http://127.0.0.1:8000/demo/students9/student111/login/
@action(methods=["get"],detail=False,url_path="student111/login")
def login(self, request):
"""
登陆视图
"""
return Response({"msg": "ok"})
# http://127.0.0.1:8000/demo/students9/1/login/log/
@action(methods=["get"],detail=True,url_path="login/log")
def login_log(self,request,pk):
# 视图集类中比普通视图类多一个属性 action
# 可以通过self.method 获取本次客户端的http请求
# 可以通过self.action获取本次客户端请求的视图方法名[ViewSet提供的]
print(self.action)
return Response({"msg":"用户活动历史记录"})
序列化嵌套
新建 app school
新建models.py
from django.db import models
from django.utils import timezone as datetime
# Create your models here.
"""
学生 sch_student 1
成绩 sch_achievement n n
课程 sch_course 1 n
老师 sch_teacher 1
"""
# 学生
class Student(models.Model):
name = models.CharField(max_length=50, verbose_name="姓名")
age = models.SmallIntegerField(verbose_name="年龄")
sex = models.BooleanField(default=False, verbose_name="性别")
class Meta:
db_table = "sch_student"
# 自定义模型
# 属性方法 可以直接当属性使用
@property
def achievement(self):
"""成绩列表"""
# print(self.s_achievement.all())
return self.s_achievement.values("student__name", "course__name", "score")
def __str__(self):
return self.name
# 课程
class Course(models.Model):
name = models.CharField(max_length=50, verbose_name="课程名称")
teacher = models.ForeignKey("Teacher", on_delete=models.DO_NOTHING, related_name="course", db_constraint=False)
class Meta:
db_table = "sch_course"
def __str__(self):
return self.name
# 教师
class Teacher(models.Model):
name = models.CharField(max_length=50, verbose_name="姓名")
sex = models.BooleanField(default=False, verbose_name="性别")
class Meta:
db_table = "sch_teacher"
def __str__(self):
return self.name
# 成绩
class Achievement(models.Model):
score = models.DecimalField(default=0, max_digits=4, decimal_places=1, verbose_name="成绩")
student = models.ForeignKey("Student", on_delete=models.DO_NOTHING, related_name="s_achievement",
db_constraint=False)
course = models.ForeignKey("Course", on_delete=models.DO_NOTHING, related_name="c_achievement", db_constraint=False)
create_time = models.DateTimeField(auto_created=datetime.now(), verbose_name="创建时间")
class Meta:
db_table = "sch_achievement"
def __str__(self):
return str(self.score)
迁移数据
python manager.py migrations
python manager.py migrate
添加数据
insert into drf.sch_teacher(id,name,sex) VALUES(1,"李老师",0);
insert into drf.sch_teacher(id,name,sex) VALUES(2,"曹老师",1);
insert into drf.sch_teacher(id,name,sex) VALUES(3,"王老师",2);
insert into drf.sch_student(id,name,age,sex) VALUES(1,"小明",18,0);
insert into drf.sch_student(id,name,age,sex) VALUES(2,"小王",19,1);
insert into drf.sch_student(id,name,age,sex) VALUES(3,"小李",17,0);
insert into drf.sch_student(id,name,age,sex) VALUES(4,"小钱",18,0);
insert into drf.sch_student(id,name,age,sex) VALUES(5,"小白",16,1);
insert into drf.sch_student(id,name,age,sex) VALUES(6,"小黑",15,1);
insert into drf.sch_student(id,name,age,sex) VALUES(7,"小赵",14,0);
insert into drf.sch_student(id,name,age,sex) VALUES(8,"小梁",13,1);
insert into drf.sch_student(id,name,age,sex) VALUES(9,"小风",21,1);
insert into drf.sch_student(id,name,age,sex) VALUES(10,"小林",22,1);
insert into drf.sch_student(id,name,age,sex) VALUES(11,"小麦",20,0);
insert into drf.sch_student(id,name,age,sex) VALUES(12,"小丽",22,0);
insert into drf.sch_student(id,name,age,sex) VALUES(13,"小石",21,1);
insert into drf.sch_student(id,name,age,sex) VALUES(14,"小丹",18,0);
insert into drf.sch_course(id,name,teacher_id) VALUES(1,"Python入门",1);
insert into drf.sch_course(id,name,teacher_id) VALUES(2,"Python进阶",2);
insert into drf.sch_course(id,name,teacher_id) VALUES(3,"Python高级",3);
insert into drf.sch_course(id,name,teacher_id) VALUES(4,"Goland入门",4);
insert into drf.sch_course(id,name,teacher_id) VALUES(5,"Goland进阶",5);
insert into drf.sch_course(id,name,teacher_id) VALUES(6,"Goland高级",6);
insert into drf.sch_achievement(create_time,id,score,course_id,student_id) VALUES("2022-03-12 08:01:09",1,100.0,1,1);
insert into drf.sch_achievement(create_time,id,score,course_id,student_id) VALUES("2022-03-12 08:01:09",2,100.0,2,2);
insert into drf.sch_achievement(create_time,id,score,course_id,student_id) VALUES("2022-03-12 08:01:09",3,100.0,3,3);
insert into drf.sch_achievement(create_time,id,score,course_id,student_id) VALUES("2022-03-12 08:01:09",4,100.0,4,4);
insert into drf.sch_achievement(create_time,id,score,course_id,student_id) VALUES("2022-03-12 08:01:09",5,100.0,5,5);
insert into drf.sch_achievement(create_time,id,score,course_id,student_id) VALUES("2022-03-12 08:01:09",6,100.0,6,6);
insert into drf.sch_achievement(create_time,id,score,course_id,student_id) VALUES("2022-03-12 08:01:09",7,100.0,7,1);
insert into drf.sch_achievement(create_time,id,score,course_id,student_id) VALUES("2022-03-12 08:01:09",8,100.0,8,2);
insert into drf.sch_achievement(create_time,id,score,course_id,student_id) VALUES("2022-03-12 08:01:09",9,100.0,9,3);
insert into drf.sch_achievement(create_time,id,score,course_id,student_id) VALUES("2022-03-12 08:01:09",10,100.0,10,4);
insert into drf.sch_achievement(create_time,id,score,course_id,student_id) VALUES("2022-03-12 08:01:09",11,100.0,11,5);
insert into drf.sch_achievement(create_time,id,score,course_id,student_id) VALUES("2022-03-12 08:01:09",12,100.0,12,6);
insert into drf.sch_achievement(create_time,id,score,course_id,student_id) VALUES("2022-03-12 08:01:09",13,100.0,13,1);
insert into drf.sch_achievement(create_time,id,score,course_id,student_id) VALUES("2022-03-12 08:01:09",14,100.0,14,2);
insert into drf.sch_achievement(create_time,id,score,course_id,student_id) VALUES("2022-03-12 08:01:09",15,100.0,1,3);
insert into drf.sch_achievement(create_time,id,score,course_id,student_id) VALUES("2022-03-12 08:01:09",16,100.0,2,4);
insert into drf.sch_achievement(create_time,id,score,course_id,student_id) VALUES("2022-03-12 08:01:09",17,100.0,3,5);
insert into drf.sch_achievement(create_time,id,score,course_id,student_id) VALUES("2022-03-12 08:01:09",18,100.0,4,6);
insert into drf.sch_achievement(create_time,id,score,course_id,student_id) VALUES("2022-03-12 08:01:09",19,100.0,5,1);
insert into drf.sch_achievement(create_time,id,score,course_id,student_id) VALUES("2022-03-12 08:01:09",20,100.0,6,2);
insert into drf.sch_achievement(create_time,id,score,course_id,student_id) VALUES("2022-03-12 08:01:09",21,100.0,7,3);
insert into drf.sch_achievement(create_time,id,score,course_id,student_id) VALUES("2022-03-12 08:01:09",22,100.0,8,4);
insert into drf.sch_achievement(create_time,id,score,course_id,student_id) VALUES("2022-03-12 08:01:09",23,100.0,9,5);
insert into drf.sch_achievement(create_time,id,score,course_id,student_id) VALUES("2022-03-12 08:01:09",24,100.0,10,6);
insert into drf.sch_achievement(create_time,id,score,course_id,student_id) VALUES("2022-03-12 08:01:09",25,100.0,11,1);
insert into drf.sch_achievement(create_time,id,score,course_id,student_id) VALUES("2022-03-12 08:01:09",26,100.0,12,2);
insert into drf.sch_achievement(create_time,id,score,course_id,student_id) VALUES("2022-03-12 08:01:09",27,100.0,13,3);
insert into drf.sch_achievement(create_time,id,score,course_id,student_id) VALUES("2022-03-12 08:01:09",28,100.0,14,4);
seriaizer.py
from rest_framework import serializers
from school.models import *
# # 老师模型 序类化器类
# class TeacherModelSerializers(serializers.ModelSerializer):
# class Meta:
# model = Teacher
# fields = "__all__"
# 成绩模型 序类化器类
class CourseModelSerializers(serializers.ModelSerializer):
class Meta:
model = Course
# 这样返回会有一个花括号 如何去除呢
fields = ["name"]
# fields = ("id", "name", "teacher")
# 成绩模型 序类化器类
class AchievementModelSerializers(serializers.ModelSerializer):
# course=CourseModelSerializers()
# 去除花括号 !! 一定要加引号
course_name = serializers.CharField(source="course.name")
teacher_name = serializers.CharField(source="course.teacher.name")
class Meta:
model = Achievement
fields = ["id", "course_name", "score", "create_time", "teacher_name"]
# fields = ("id", "course","score", "create_time")
# 成绩模型 序类化器类
class AchievementModelSerializers2(serializers.ModelSerializer):
class Meta:
model = Achievement
fields = "__all__"
"""
制定关联深度
# 1.成绩模型->课程 1
# 2.从老师模型->课程->成绩 2
# 3.从老师模型->课程->成绩->学生 3
"""
depth=3
# 学生模型 序类化器类
class StudentModelSerializers(serializers.ModelSerializer):
# s_achievement 就是 models 里指明的外键字段 ,不可以自定义 不能h指明序列化器
# s_achievement = AchievementModelSerializers2(many=True)
class Meta:
model = Student
fields = ("id", "name", "achievement")
# fields = ("id", "name", "s_achievement")
views.py
from .models import *
from rest_framework.viewsets import ModelViewSet
from .serializers import StudentModelSerializers
class StudentModelViewSet(ModelViewSet):
queryset = Student.objects.all()
serializer_class = StudentModelSerializers
urls.py
from django.urls import path
from .views import *
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register("students", StudentModelViewSet, basename="students")
urlpatterns = [
] + router.urls
DRF 组件
认证组件 Authentication
新建 app opt
python manage.py startapp opt
新建管理员
python manage.py createsuperuser
常用的认证有三种
全局配置如下
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
# 基础认证
'rest_framework.authentication.BasicAuthentication',
# session认证
'rest_framework.authentication.SessionAuthentication',
# # token认证
# 'rest_framework.authentication.TokenAuthentication',
# 自定义验证
'drfDemo.authentication.CustomAuthentication'
),
}
局部设置
views.py
from rest_framework.authentication import BaseAuthentication,SessionAuthentication
from rest_framework.views import APIView
class Example(APIView):
# 类属性
authentication_classes = [SessionAuthentication,BaseAuthentication]
def get(self,request):
pass
验证失败 会有两种情况
- 401 未认证
- 403 权限被禁止
自定义组件
from rest_framework.authentication import BaseAuthentication
from django.contrib.auth import get_user_model
class CustomAuthentication(BaseAuthentication):
"""
自定义认证类
"""
def authenticate(self, request):
"""
认证方法
# request 本次客户端发来的http请求
"""
user = request.query_params.get("user")
pwd = request.query_params.get("pwd")
if user == "root" and pwd == "admin*123":
return None
# get_user_model 获取系统中用户表对应的用户模型类
user = get_user_model().objects.all()
# 按照格式返回
return {user: None}
添加到全局配置中
from rest_framework.authentication import BasicAuthentication,SessionAuthentication
from rest_framework.views import APIView
from rest_framework.response import Response
from drfDemo.authentication import CustomAuthentication
class Example(APIView):
# 类属性
authentication_classes = [CustomAuthentication,SessionAuthentication,BasicAuthentication]
def get(self,request):
# AnonymousUser 未登录用户
print(request.user)
# <class 'django.contrib.auth.models.AnonymousUser'>
# print(type(request.user))
if request.user.id:
print("已通过验证")
else:
print("未通过验证")
return Response({"msg":"ok"})
权限 Permission
权限控制 可以限制用户对于试图的访问 和对于具有模型对象的访问
- 在执行视图控制的as_views()方法的dispatch方法前,会进行视图访问权限的判断
- 在通过get_object() 获取具体模型对象时,会进行模型对象访问权限的判断
可以使用该DEFAULT_PERMISSION_CLASSES设置在全局范围内设置默认权限策略。例如。
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
)
}
如果未指定,则此设置默认为允许无限制访问:
'DEFAULT_PERMISSION_CLASSES': (
# 默认允许所有用户
'rest_framework.permissions.AllowAny',
)
提供的权限——权限级别
IsAuthenticated
表示仅仅允许身份验证通过的用户访问,其他用户无法访问。
IsAuthenticatedOrReadOnly
表示仅仅允许身份验证通过的用户访问,或者只允许只读请求(GET请求)访问。
IsAdminUser
表示仅仅允许管理员用户访问,普通用户无法访问。
views.py
from rest_framework.permissions import IsAuthenticated,IsAdminUser,IsAuthenticatedOrReadOnly
class HomeInfoAPIView(APIView):
authentication_classes = [CustomAuthentication, ]
# permission_classes = [IsAuthenticated] # 表示当前视图类中的视图方法只能被已登录的 的站点会员访问
# permission_classes = [IsAdminUser] # 表示当前视图类中的视图方法 只能被管理员访问
permission_classes = [IsAuthenticatedOrReadOnly] # 表示当前视图类中的视图方法 游客只能查看 不能修改
def get(self, request):
return Response({"msg": "ok"})
def post(self, request):
return Response({"msg": "ok"})
自定义权限
如果需要自定义权限 需要 继承rest_framework.permssion.BasePermssion 父类,并实现阿以下任意的一个方法或者全部
.has_permission(self,request,view)
是否可以访问视图 view 表示当前视图对象
class IsXiaoMingPermission(BasePermission):
def has_permission(self, request, view):
"""
自定义选项
返回结果为True 则表示允许访问视图类
request:本次客户端提交的请求对象
views: 本次客户端访问的视图类
"""
# 写在自己要实现的代码过程,返回值为True,则表示通行
# user = request.query_params.get("user")
# return user == "xiaoming"
return bool(request.user and request.user.username == "xiaoming")
.has_object_permission(self,request,view,obj)
是否可以访问模型对象,view表示当前视图,obj为模型数据对象
def has_object_permission(self, request, view, obj):
"""
模型权限,写了视图权限(has_permission)方法 一般就 不需要写这个方法了
返回结果为True 则表示允许访问
request:本次客户端提交的请求对象
views: 本次客户端访问的视图类
obj:本次权限判断的模型对象
"""
from school.models import Student
if isinstance(obj, Student):
# 限制只有小明才能操作的Student模型表
user = request.query_params.get("user")
return user == "xiaoming"
else:
# 操作其他模型 直接放行
return True
只允许小明操作
from rest_framework.generics import RetrieveAPIView, ListCreateAPIView
class StudentInfoApiView(RetrieveAPIView):
queryset = Student.objects.all()
serializer_class = StudentModelSerializers
# 自定义权限
permission_classes = [IsXiaoMingPermission]
访问
http://127.0.0.1:8000/opt/studentinfo/2?user=xiaoming
settings.py
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
# 但是会有个小问题 就是你在登陆页面 会让你去登陆 可以使用在登陆的视图类中设置 permission_classes=[]
'rest_framework.permissions.IsAuthenticated', # 大部分公司企业的内部站点都是使用这个 不允许其他人访问 都会默认全局设置
# 'drfDemo.permission.IsXiaoMingPermission',
)
}
限流Throttling
可以对接口访问的频次进行限制 以减轻服务器压力,或者实现特定的业务
一般用于付费购买次数 投票等场景
settings.py
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES': (
'rest_framework.throttling.AnonRateThrottle', # 未登录用户
'rest_framework.throttling.UserRateThrottle' #登录用户
),
'DEFAULT_THROTTLE_RATES': {
'anon': '100/day', # 未登录用户 每天100次
'user': '1000/day' # 登录用户每天1000次
}
}
一般是处于局部配置
可选限流类
- AnonRateThrottle
限制所有匿名未认证用户,使用IP区分用户。
使用DEFAULT_THROTTLE_RATES['anon'] 来设置频次
- UserRateThrottle
限制认证用户,使用User id 来区分。
使用DEFAULT_THROTTLE_RATES['user'] 来设置频次
- ScopedRateThrottle
限制用户对于每个视图的访问频次,使用ip或user id。
class ContactListView(APIView):
throttle_scope = 'contacts'
...
class ContactDetailView(APIView):
throttle_scope = 'contacts'
...
class UploadView(APIView):
throttle_scope = 'uploads'
...
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES': (
'rest_framework.throttling.ScopedRateThrottle',
),
'DEFAULT_THROTTLE_RATES': {
'contacts': '1000/day',
'uploads': '20/day'
}
}
filter过滤
安装
pip install django-filter
在Django的项目配置文件中安装并配置django_filters应用:
INSTALLED_APPS = [
...
'django_filters',
]
REST_FRAMEWORK = {
# 过滤器默认后端
'DEFAULT_FILTER_BACKENDS': (
'django_filters.rest_framework.DjangoFilterBackend',),
}
局部配置
from django_filters.rest_framework import DjangoFilterBackend
class Demo4ApiView(ListCreateAPIView):
queryset = Student.objects.all()
serializer_class = StuApiModelSerializers
# 局部配置
filter_backends = [BasicAuthentication]
filter_fields = ["name", "gender"]
自定义filter
需要依赖第三方库django-filter
- 安装:
pip install django-filter
settings.py
中 添加INSTALLED_APPS = [...,'django_filters',...]
- 创建
filters.py
文件 代码如下
import django_filters
from .models import Goods
class GoodsFilter(django_filters.rest_framework.FilterSet):
min_price = django_filters.NumberFilter(name='shop_price', lookup_expr='gt')
type = filters.CharFilter(method='filter_type') # 指定对应的方法
def filter_type(self, queryset, name, value):
#多选项,由,号分割
return queryset.filter(feature__in=value.split(","))
class Meta:
model = Goods
fields = ['min_price', ‘type’, 'status']
views.py
文件中加入filter_class
from .filters import GoodsFilter
class xxView(xxxView):
filter_class = GoodsFilter
搜索 search
我们用drf自带的SearchFilter
可以实现该功能
from rest_framework import filters
class UserListView(generics.ListAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
# backends 中加入SearchFilter 才能激活搜索,字段为 search
filter_backends = (filters.SearchFilter,)
search_fields = ('username', 'email',‘profile__profession’)
排序 Ordering
对于列表数据 Rest framework 提供了OrderingFilter过滤器来帮助完美快速指名字段进行排序
使用方法:
在类视图中色湖之 filter_backends,使用rest_framework.filters.OrderingFilter过滤器,REST framework会在请求的查询字符串中检查是否包含了ordering参数,如果包含了 ordering参数,则按照ordering参数指明的排序字段对数据集进行排序
前端可以传递的ordering参数的可选字段需要在ordering_fileds中指明
配置settings.py
同search一样,需要drf自带的OrderingFilter
。使用方法如下
settings.py
# 过滤器默认后端
'DEFAULT_FILTER_BACKENDS': (
'rest_framework.filters.OrderingFilter',
),
局部配置
class UserListView(generics.ListAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
# backend 中加入OrderingFilter 激活ordering filter,字段为ordering
filter_backends = (filters.OrderingFilter,)
ordering_fields = ('account', 'username', 'email')
# 指定默认的排序字段
ordering = ('username',)
如果需要取倒序,可以在字段前加“-” 如 http://example.com/api/users?ordering=-username
, 如果需要多个排序的,可以使用“,”分隔 如:http://example.com/api/users?ordering=account,username
!!!注意 要不就都是全局 要不就都是局部 不能你一个局部 我一个全局
分页 Pagination
REST框架支持自定义分页风格,你可以修改每页显示数据集合的最大长度。
分页链接支持以下两种方式提供给用户:
-
分页链接是作为响应内容提供给用户
-
分页链接被包含在响应头中(Content-Range或者Link)
内建风格使用作为响应内容提供给用户。这种风格更容易被使用可浏览API的用户所接受
如果使用通用视图或者视图集合。系统会自动帮你进行分页。如果使用的是APIView,你就需要自己调用分页API,确保返回一个分页后的响应。可以将pagination_class设置为None关闭分页功能。
可以通过设置DEFAULT_PAGINATION_CLASS和PAGE_SIZE,设置全局变量。
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS':'rest_framework.pagination.LimitOffsetPagination',
'PAGE_SIZE': 100
}
需要同时设置pagination class和page size。
也可以在单个视图中设置pagination_class属性,一般你需要使用统一的分页风格。
自定义分页器
class CustomPagination(PageNumberPagination):
page_size = 1000
page_size_query_param = 'page_size'
max_page_size = 10000
# 然后在视图中使用.pagination_class属性调用该自定义类
class CustomVim(generics.ListAPIView):
queryset = Billing.objects.all()
serializer_class = BillingRecordsSerializer
pagination_class = CustomPagination
# 或者是在设置中修改DEFAULT_PAGINATION_CLASS
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'apps.core.pagination.CustomPagination'
}
异常处理Exception
Rest framework 本身在APIView提供了异常处理,但是仅针对与drf内部现有的接口开发相关的一场进行格式处理,但是开发中完美会使用各种数据以及各种网络请求,这样就有可能出现异常,这些异常在drf中是没有进行处理的 所以就会冒泡给django框架了,django 会组织各种错误信息,然后返回html给i客户端,所以在前后端分离项目中,可能js无法理解或者无法接受这种数据,甚至导致js出现错误的情况,因此未来避免这种情况,我们可以自定义一个属于自己的异常处理函数,对drf无法处理的异常,我们编写异常处理的代码逻辑
针对现有的drf异常处理添加属于开发者自己的逻辑代码,一般是我们编写的异常处理函数 会写道一个公共的目录或者主应用目录下,这里主应用下直接创建一个exceptions.py
# 获取处理异常的句柄(方法)
# 一层层看源码,走的是配置文件,拿到的是rest_framework.views的exception_handler
# 自定义:直接写exception_handler函数,在自己的配置文件配置EXCEPTION_HANDLER指向自己的
exception_handler = self.get_exception_handler()
# 异常处理的结果
# 自定义异常就是提供exception_handler异常处理函数,处理的目的就是让response一定有值
response = exception_handler(exc, context)
自定义异常处理
在settings.py文件中配置
REST_FRAMEWORK = {
# 全局配置异常模块 'EXCEPTION_HANDLER':'drfDemo.exceptions.exceptions_custom',
}
注: utils是自建的文件夹,里面是自己写的封装功能
utils/exception.py文件
from rest_framework.response import Response
from rest_framework.views import exception_handler
from django.db import DataError
def exceptions_custom(exc,context):
"""
自定义异常
exc: 本次发生的异常
context: 本次发生的异常的上下文环境
所谓的执行上下文就是python解释器在执行代码时保存在内存中的变量,函数,类,对象,模块等一切信息
"""
response=exception_handler(exc,context)
if response:
pass
else:
if isinstance(exc,ZeroDivisionError):
response= Response({"detail":"不能被0除"})
if isinstance(exc,DataError):
response= Response({"detail":"数据库存储异常"})
return response
- REST framework定义的异常
- APIException 所有异常的父类
- ParseError 解析错误
- AuthenticationFailed 认证失败
- NotAuthenticated 尚未认证
- PermissionDenied 权限决绝
- NotFound 未找到
- MethodNotAllowed 请求方式不支持
- NotAcceptable 要获取的数据格式不支持
- Throttled 超过限流次数
- ValidationError 校验失败
自动生成接口文档
coreapi
安装依赖
REST framewrok生成接口文档需要coreapi
库的支持。
pip install -i https://pypi.douban.com/simple/ coreapi
然后 重启 django进程。
在项目根urls.py中增加如下2行红色字体的内容:
from django.contrib import admin
from django.urls import include, path
from rest_framework.documentation import include_docs_urls
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('project.urls')),
path('docs/', include_docs_urls(title='说明文档')),
]
然后,在项目 settings.py中增加如下3行,不然会提示 'AutoSchema' object has no attribute 'get_link':
REST_FRAMEWORK = { 'DEFAULT_SCHEMA_CLASS':'rest_framework.schemas.coreapi.AutoSchema'
}
yasg
安装
pip install -i https://pypi.douban.com/simple/ drf_yasg
配置settings.py
import drf_yasg
INSTALLED_APPS = [
....
'drf_yasg',
]
路由 urls.py
from django.contrib import admin
from django.urls import path, include, re_path
from rest_framework.documentation import clude_docs_urls
from drf_yasg.views import get_schema_view
from drf_yasg import openapi
schema_view = get_schema_view(
openapi.Info(
title = 'API接口文档',
default_version='v1',
description='接口文档平台',
# terms_of_service='http://api.xxx.com',
contact=openapi.Contact(email='xxxx@qq.com'),
license=openapi.License(name='License')
),
public=True
# 权限类
# permission_classes = (permissions.AllowAny),
)
urlpatterns = [
re_path(r'^swagger(?P<format>\.json|\.yaml)$',
schema_view.without_ui(cache_timeout=0), name=
'schema-json'),
path('swagger/', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger'
),
path('redoc/', schema_view.with_ui('redoc', cache_timeout=0),
name='schema-redoc')
]
直接登录会报403
所以路径为
``