Django REST framework(DRF)

Created
Sep 6, 2023 05:31 AM
Tags
重要
目录
 
详细内容看下面链接
以下内容纯属个人编写

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({})
效果图
notion image
 
notion image
 

单字段进行验证

写在 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({})
效果图
notion image
 

外部验证函数

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)
效果如下
 
notion image

反序列化 增加和更新操作

增加

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 弗雷
      APIViewView不同之处在于
      • 传入的视图方法是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次 } }
                一般是处于局部配置
                可选限流类
                1. AnonRateThrottle
                限制所有匿名未认证用户,使用IP区分用户。
                使用DEFAULT_THROTTLE_RATES['anon'] 来设置频次
                1. UserRateThrottle
                限制认证用户,使用User id 来区分。
                使用DEFAULT_THROTTLE_RATES['user'] 来设置频次
                1. 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
                所以路径为
                ``