目录
drf 序列化器什么是序列化与反序列化drf序列化与反序列化正常显示页面数据校验的方法多个字段进行验证单字段进行验证外部验证函数反序列化 增加和更新操作增加更新附加说明模型序列化器 的操作和使用drf 中视图调用的http请求和响应处理类基本视图类APIView和通用视图类GenericAPIView的声明和使用APIView基本视图类GenericAPIView(通用视图类)基于Mixin模型扩展类+GenericView 通用类视图子类基于视图集和路由集实现API接口视图集ViewSet通用视图集GenericViewSet通用视图集+mixin混合类路由Routers序列化嵌套DRF 组件认证组件 Authentication权限 PermissionIsAuthenticatedIsAuthenticatedOrReadOnlyIsAdminUser限流Throttlingfilter过滤自定义filter搜索 search排序 Ordering分页 Pagination异常处理Exception自动生成接口文档coreapiyasg
详细内容看下面链接
以下内容纯属个人编写
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)
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)
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 或者请求其他方式的方法
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", }, } }
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)
# 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
# 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
表示仅仅允许管理员用户访问,普通用户无法访问。
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
可以对接口访问的频次进行限制 以减轻服务器压力,或者实现特定的业务
一般用于付费购买次数 投票等场景
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中指明
同search一样,需要drf自带的
OrderingFilter
。使用方法如下# 过滤器默认后端 '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
所以路径为
``