参考:
为什么要学DRF和什么是REST API | 大江狗的博客
上一章:
一、Django REST Framework (DRF)& RESTful 风格api_做测试的喵酱的博客-CSDN博客
一、DRF框架介绍
1.1 介绍
Django REST Framework (DRF)这个神器我们可以快速开发出优秀而且规范的Web API来。Django REST framework 给Django提供了用于构建Web API 的强大而灵活的工具包, 包括:
- 序列化器Serializer,可以高效的进行序列化与反序列化操作。
- 认证:多种身份认证
- 权限
- 分页
- 过滤
- 限流
- 可扩展,插件丰富
- 提供了极为丰富的类视图、Mixin扩展类、ViewSet视图集。
1.2 安装与配置
1、安装
pip install djangorestframework
其他插件:(非必需)
pip install markdown
2、注册应用
INSTALLED_APPS = [
'rest_framework',
]
二、演示数据准备
1、创建子应用miaoschool,注册应用
2、创建2个模型类,班级模型类MiaoClass,学生信息模型类MiaoStudent
models.py
from django.db import models
# Create your models here.
class MiaoClass(models.Model):
id = models.AutoField(primary_key=True, verbose_name='id', help_text='id')
classname = models.CharField(max_length=20, verbose_name='班级名称', help_text='班级名称')
clasleader = models.CharField(max_length=10, verbose_name='班主任姓名', help_text='班主任姓名')
classcode = models.IntegerField(unique=True, verbose_name='班级code', help_text='班级code')
ifopens = models.BooleanField(default=True, verbose_name='是否开学', help_text='是否开学')
classrate = models.IntegerField( verbose_name='班费', help_text='班费')
class Meta:
# i.db_table指定创建的数据表名称
db_table = 'tb_class'
# 为当前数据表,设置中午呢描述
verbose_name = "班级表"
verbose_name_plural = "班级表"
class MiaoStudent(models.Model):
sname = models.CharField(max_length=20, verbose_name='学生姓名', help_text='学生姓名')
sgender = models.BooleanField(verbose_name='性别', help_text='性别')
sage = models.IntegerField(verbose_name='年龄', help_text='年龄')
sid = models.IntegerField(unique=True, verbose_name='学号', help_text='学号')
sscore = models.IntegerField( verbose_name='成绩', help_text='成绩')
classid = models.ForeignKey('miaoschool.MiaoClass',on_delete=models.CASCADE,verbose_name='班级id', help_text='班级id')
class Meta:
# i.db_table指定创建的数据表名称
db_table = 'tb_student'
# 为当前数据表,设置中午呢描述
verbose_name = "学生信息表"
verbose_name_plural = "学生信息表"
插入数据:
-- ----------------------------
-- Records of tb_class
-- ----------------------------
BEGIN;
INSERT INTO `tb_class` (`id`, `classname`, `clasleader`, `classcode`, `ifopens`, `classrate`) VALUES (1, '一班', '王老师', 1001, 1, 200);
INSERT INTO `tb_class` (`id`, `classname`, `clasleader`, `classcode`, `ifopens`, `classrate`) VALUES (2, '二班', '欧阳老师', 1006, 1, 300);
INSERT INTO `tb_class` (`id`, `classname`, `clasleader`, `classcode`, `ifopens`, `classrate`) VALUES (3, '三班', '东风老师', 1007, 1, 400);
INSERT INTO `tb_class` (`id`, `classname`, `clasleader`, `classcode`, `ifopens`, `classrate`) VALUES (4, '四班', '白腊图老师', 1011, 1, 20);
INSERT INTO `tb_class` (`id`, `classname`, `clasleader`, `classcode`, `ifopens`, `classrate`) VALUES (5, '五班', '张三丰老师老师', 1021, 1, 150);
COMMIT;
-- ----------------------------
-- Records of tb_student
-- ----------------------------
BEGIN;
INSERT INTO `tb_student` (`id`, `sname`, `sgender`, `sage`, `sid`, `sscore`, `classid_id`) VALUES (1, '阿珍', 1, 18, 101, 100, 1);
INSERT INTO `tb_student` (`id`, `sname`, `sgender`, `sage`, `sid`, `sscore`, `classid_id`) VALUES (2, '李珍', 1, 18, 104, 100, 1);
INSERT INTO `tb_student` (`id`, `sname`, `sgender`, `sage`, `sid`, `sscore`, `classid_id`) VALUES (3, '孙珍', 1, 18, 102, 100, 1);
INSERT INTO `tb_student` (`id`, `sname`, `sgender`, `sage`, `sid`, `sscore`, `classid_id`) VALUES (7, '李爱', 1, 18, 106, 100, 1);
INSERT INTO `tb_student` (`id`, `sname`, `sgender`, `sage`, `sid`, `sscore`, `classid_id`) VALUES (9, '张三', 0, 18, 2301, 20, 2);
INSERT INTO `tb_student` (`id`, `sname`, `sgender`, `sage`, `sid`, `sscore`, `classid_id`) VALUES (10, '李爱', 1, 18, 107, 100, 1);
INSERT INTO `tb_student` (`id`, `sname`, `sgender`, `sage`, `sid`, `sscore`, `classid_id`) VALUES (11, '张三', 0, 18, 2302, 20, 2);
INSERT INTO `tb_student` (`id`, `sname`, `sgender`, `sage`, `sid`, `sscore`, `classid_id`) VALUES (12, '李三', 1, 19, 2325, 30, 2);
INSERT INTO `tb_student` (`id`, `sname`, `sgender`, `sage`, `sid`, `sscore`, `classid_id`) VALUES (13, '张红', 0, 20, 2303, 40, 2);
INSERT INTO `tb_student` (`id`, `sname`, `sgender`, `sage`, `sid`, `sscore`, `classid_id`) VALUES (14, '张珍', 1, 16, 2304, 50, 3);
INSERT INTO `tb_student` (`id`, `sname`, `sgender`, `sage`, `sid`, `sscore`, `classid_id`) VALUES (15, '刘慧珍', 0, 17, 2305, 60, 3);
INSERT INTO `tb_student` (`id`, `sname`, `sgender`, `sage`, `sid`, `sscore`, `classid_id`) VALUES (16, '王莫涵', 1, 18, 2306, 70, 3);
INSERT INTO `tb_student` (`id`, `sname`, `sgender`, `sage`, `sid`, `sscore`, `classid_id`) VALUES (17, '张茜茜', 0, 20, 2307, 80, 4);
INSERT INTO `tb_student` (`id`, `sname`, `sgender`, `sage`, `sid`, `sscore`, `classid_id`) VALUES (18, '学姐', 1, 15, 2308, 90, 4);
INSERT INTO `tb_student` (`id`, `sname`, `sgender`, `sage`, `sid`, `sscore`, `classid_id`) VALUES (19, '张绍京', 0, 14, 2309, 100, 4);
INSERT INTO `tb_student` (`id`, `sname`, `sgender`, `sage`, `sid`, `sscore`, `classid_id`) VALUES (20, '董超', 1, 20, 2311, 20, 5);
INSERT INTO `tb_student` (`id`, `sname`, `sgender`, `sage`, `sid`, `sscore`, `classid_id`) VALUES (21, '吕布', 0, 21, 2321, 40, 5);
INSERT INTO `tb_student` (`id`, `sname`, `sgender`, `sage`, `sid`, `sscore`, `classid_id`) VALUES (22, '黄大仙', 1, 17, 2322, 60, 5);
COMMIT;
三、序列化器Serializer
3.1 序列化&反序列化
每种编程语言都有各自的数据类型, 将属于自己语言的数据类型或对象转换为可通过网络传输或可以存储到本地磁盘的数据格式(如:XML、JSON或特定格式的字节串)的过程称为序列化(seralization);反之则称为反序列化。
API开发的本质就是各种后端语言的自己的数据类型序列化为通用的可读可传输的数据格式,比如常见的JSON类型数据。
3.2 Python数据序列化
举个简单例子。Python自带json模块的dumps方法可以将python常用数据格式(比如列表或字典)转化为json格式,如下所示。你注意到了吗? 生成的json格式数据外面都加了单引号,这说明dict类型数据已经转化成了json字符串。
>>> import json
>>> json.dumps({"name":"John", "score": 112})
'{"name": "John", "score": 112}'
3.3 DRF中的序列化与反序列化
序列化: 将 查询集QuerySet (多个模型类的集合)与单个模型类的实例,转化为 json数据(字符串)。
反序列化:将json格式的字符串数据,转换为Django中的模型类的对象。
3.4 DRF框架中实现序列化
1、在应用中,创建一个名为 serializers.py 的文件。(推荐命名为serializers.py,其他命名也可以)
3.4.1 定义序列化器类
1、定义序列化器类,必须得继承Serializer类或者Serializer子类
from rest_framework import serializers
class StudentSerializer(serializers.Serializer):
2、定义的序列化器类中,字段名要与模型类中的字段名保持一致。(字段个数可以不一致,但是字段名称一定要保持一致)
3、定义的序列化器类的字段(类属性)为Field子类
4、序列化器类中,定义了哪些字段,在序列化与反序列化中,只处理这些定义的字段,其他字段不处理。
5.常用的序列化器字段类型。
跟模型类差不多
数据类型 | Field类型 |
int | IntegerField |
str | CharField |
bool | BooleanField |
datetime | DateTimeField |
6、序列化器字段中常用的选项
a、 label和help_text,与模型类中的verbose_name和help_text参数一样
b、(用于数据校验) IntegerField,可以使用max_value指定最大值,min_value指定最小值
c、 (用于数据校验)CharField,可以使用max_length指定最大长度,min_length指定最小长度
3.4.2 序列化器字段中常用的选项(数据校验)
a、 label和help_text,与模型类中的verbose_name和help_text参数一样
b、(用于数据校验) IntegerField,可以使用max_value指定最大值,min_value指定最小值
c、 (用于数据校验)CharField,可以使用max_length指定最大长度,min_length指定最小长度
四、序列化
4.1 应用场景:
用户请求数据,从数据库取出数据,反给用户。ORM框架,从数据库中取的数据是QuerySet。
通过序列化将QuerySet 转化为josn数据返回给用户。
4.2 创建序列化器类
序列化器类,字段与模型类字段基本一致,用来做将模型类实例与json数据相互转换的中间件。
serializers.py
# -*- coding:utf-8 -*-
# @Author: 喵酱
# @time: 2023 - 05 -21
# @File: serializers.py
# desc: 序列化器
from rest_framework import serializers
class StudentSerializer(serializers.Serializer):
id = serializers.IntegerField(label='学生id', help_text='学生id', max_value=1000, min_value=1)
sname = serializers.CharField(label='学生姓名', help_text='学生姓名',max_length=10,min_length=1 )
sgender =serializers.BooleanField(label='学生性别', help_text='学生性别',)
sage = serializers.IntegerField(label='学生年龄', help_text='学生年龄',max_value=100, min_value=1)
sid = serializers.IntegerField(label='学生学号', help_text='学生学号',)
sscore = serializers.IntegerField(label='学生成绩', help_text='学生成绩', max_value=100, min_value=0)
# 注意,转换外键的值时,输入使用类属性名称来转化,接收到的是一个主表的实例对象
classid = serializers.CharField(label='班级id', help_text='班级id',)
# 注意,转换外键的值时,输入使用从表中外键的值来转化,接收到的是一个真正的数值
classid_id = serializers.IntegerField(label='班级id', help_text='班级id', max_value=10, min_value=1)
注意外键的处理:
# 注意,转换外键的值时,输入使用类属性名称来转化,接收到的是一个主表的实例对象
classid = serializers.CharField(label='班级id', help_text='班级id',)
# 注意,转换外键的值时,输入使用从表中外键的值来转化,接收到的是一个真正的数值
classid_id = serializers.IntegerField(label='班级id', help_text='班级id', max_value=10, min_value=1)
4.3 读取多条数据,处理Queryset
一、视图函数的处理。
def get_student_all(request):
queryset=MiaoStudent.objects.all()
print(queryset)
serializer = StudentSerializer(instance=queryset, many=True)
return JsonResponse(serializer.data, safe=False)
注意:
1、 QuerySet 是多个模型类实例的集合。返回给用户的数据格式不是json,而是一个列表,每个元素是一个json。
2、处理QuerySet,一定要使用many=True
serializer = StudentSerializer(instance=queryset, many=True)
3、将序列化对象实例,转化为json的列表。
serializer.data 是序列化后的数据
JsonResponse(serializer.data, safe=False)
二、请求返回数据
[
{
"id": 1,
"sname": "阿珍",
"sgender": true,
"sage": 18,
"sid": 101,
"sscore": 100,
"classid": "MiaoClass object (1)",
"classid_id": 1
},
{
"id": 2,
"sname": "李珍",
"sgender": true,
"sage": 18,
"sid": 104,
"sscore": 100,
"classid": "MiaoClass object (1)",
"classid_id": 1
},
{
"id": 3,
"sname": "孙珍",
"sgender": true,
"sage": 18,
"sid": 102,
"sscore": 100,
"classid": "MiaoClass object (1)",
"classid_id": 1
},
{
"id": 7,
"sname": "李爱",
"sgender": true,
"sage": 18,
"sid": 106,
"sscore": 100,
"classid": "MiaoClass object (1)",
"classid_id": 1
},
{
"id": 9,
"sname": "张三",
"sgender": false,
"sage": 18,
"sid": 2301,
"sscore": 20,
"classid": "MiaoClass object (2)",
"classid_id": 2
},
{
"id": 10,
"sname": "李爱",
"sgender": true,
"sage": 18,
"sid": 107,
"sscore": 100,
"classid": "MiaoClass object (1)",
"classid_id": 1
},
{
"id": 11,
"sname": "张三",
"sgender": false,
"sage": 18,
"sid": 2302,
"sscore": 20,
"classid": "MiaoClass object (2)",
"classid_id": 2
},
{
"id": 12,
"sname": "李三",
"sgender": true,
"sage": 19,
"sid": 2325,
"sscore": 30,
"classid": "MiaoClass object (2)",
"classid_id": 2
},
{
"id": 13,
"sname": "张红",
"sgender": false,
"sage": 20,
"sid": 2303,
"sscore": 40,
"classid": "MiaoClass object (2)",
"classid_id": 2
},
{
"id": 14,
"sname": "张珍",
"sgender": true,
"sage": 16,
"sid": 2304,
"sscore": 50,
"classid": "MiaoClass object (3)",
"classid_id": 3
},
{
"id": 15,
"sname": "刘慧珍",
"sgender": false,
"sage": 17,
"sid": 2305,
"sscore": 60,
"classid": "MiaoClass object (3)",
"classid_id": 3
},
{
"id": 16,
"sname": "王莫涵",
"sgender": true,
"sage": 18,
"sid": 2306,
"sscore": 70,
"classid": "MiaoClass object (3)",
"classid_id": 3
},
{
"id": 17,
"sname": "张茜茜",
"sgender": false,
"sage": 20,
"sid": 2307,
"sscore": 80,
"classid": "MiaoClass object (4)",
"classid_id": 4
},
{
"id": 18,
"sname": "学姐",
"sgender": true,
"sage": 15,
"sid": 2308,
"sscore": 90,
"classid": "MiaoClass object (4)",
"classid_id": 4
},
{
"id": 19,
"sname": "张绍京",
"sgender": false,
"sage": 14,
"sid": 2309,
"sscore": 100,
"classid": "MiaoClass object (4)",
"classid_id": 4
},
{
"id": 20,
"sname": "董超",
"sgender": true,
"sage": 20,
"sid": 2311,
"sscore": 20,
"classid": "MiaoClass object (5)",
"classid_id": 5
},
{
"id": 21,
"sname": "吕布",
"sgender": false,
"sage": 21,
"sid": 2321,
"sscore": 40,
"classid": "MiaoClass object (5)",
"classid_id": 5
},
{
"id": 22,
"sname": "黄大仙",
"sgender": true,
"sage": 17,
"sid": 2322,
"sscore": 60,
"classid": "MiaoClass object (5)",
"classid_id": 5
}
]
4.4 读取单条数据,处理模型类的实例
一、视图函数的处理。
def get_student_one(request):
obj=MiaoStudent.objects.get(id=10)
serializer = StudentSerializer(instance=obj)
return JsonResponse(serializer.data)
注意:
obj 是一个模型类的实例。
将模型类的实例,序列化
serializer = StudentSerializer(instance=ojb)
serializer.data 是序列化后的数据
将序列化对象实例,转化为json的数据。
JsonResponse(serializer.data, safe=False)
五、反序列化
5.1 应用场景:
用户传入json请求数据,先将用户传入的json 字符串,转为字典,再将这个字典反序列化转化为模型类的实例,然后再调用ORM框架,执行sql。
比如创建学生信息,用户输入一个json,然后将这个json生成一行表的数据。
5.2 数据处理
1、获取接口请求json参数,并转化为字典。
我们手动对输入数据做一次简单的校验,是否能转成字典
def get_json(request):
try:
# 1、获取接口请求json参数,并转化为字典
data = json.loads(request.body)
except Exception as e:
return JsonResponse({"msg":"参数有误"},status=400)
5.3 数据校验
序列化器提供了数据校验功能。
定义序列化器类,使用data关键字参数传递字典参数。
1、序列化器类:
在创建序列化器类时,如
sscore = serializers.IntegerField(label='学生成绩', help_text='学生成绩', max_value=100, min_value=0)
指定了sscore字段的校验规则,大于等于0,小于等于100。
sname = serializers.CharField(label='学生姓名', help_text='学生姓名',max_length=10,min_length=1 )
指定了sname字段的校验规则,最少1个字符,最多10个字符。
2、调用校验功能:
序列化器对象调用.is_valid()方法,才会开始对前端输入的参数进行校验
# 获取反序列化的实例对象
student_obj=StudentSerializer(data=data)
调用校验方法
student_obj.is_valid()
返回值为布尔类型,校验通过为True,校验失败为False。
3、获取校验失败的报错信息:
.只有在调用.is_valid()方法之后,才可以使用序列化器对象调用.errors属性,来获取错误提示信息(字典类型)
只有在调用
student_obj.is_valid()
校验之后,才可以查看报错信息,不然报错。
查看报错信息
student_obj.errors
4、在校验不通过时,直接抛出异常。
调用.is_valid()方法,添加raise_exeception=True,校验不通过会抛出异常。
student_obj.is_valid(raise_exeception=True)
5、校验之后的数据.validated_data
student_obj.validated_data
只有在调用.is_valid()方法之后,才可以使用序列化器对象调用.validated_data属性,来获取校验通过之后的数据。
.validated_data 数据与 使用json.load转化之后的数据有区别
6、整体代码:
def post_json(request):
try:
# 1、获取接口请求json参数,并转化为字典
data = json.loads(request.body)
except Exception as e:
return JsonResponse({"msg":"参数有误"},status=400)
# 获取反序列化的实例对象
student_obj=StudentSerializer(data=data)
# 对数据进行校验
# 校验不通过
if not student_obj.is_valid():
return JsonResponse(student_obj.errors,status=400)
# 创建数据,执行saql
MiaoStudent.objects.create(**student_obj.validated_data)
return JsonResponse({"msg":"创建成功"})
7、功能演示
a、传入sscore大于100的数字
http://127.0.0.1:8000/post_json/
参数:
{
"sname":"浩轩",
"sgender":1,
"sage":17,
"sid":2046,
"sscore":101,
"id":26,
"classid_id":3
}
返回结果:
{
"sscore": [
"Ensure this value is less than or equal to 100."
]
}
b、传入ssname长度为11位字符
传参:
{
"sname":"浩哈哈哈哈哈哈哈哈哈哈哈哈哈哈轩",
"sgender":1,
"sage":17,
"sid":2046,
"sscore":101,
"id":26,
"classid_id":3
}
返回结果:
{
"sname": [
"Ensure this field has no more than 10 characters."
],
"sscore": [
"Ensure this value is less than or equal to 100."
]
}
疑问:
为啥我的提示是英文?明天查查。