😁
运维笔记
运维笔记
运维笔记
  • Welcome my notes
  • PYTHON
    • Python 小技巧
      • Python阿里云余额TG报警
      • Python应知小技巧:如何用更少的代码做更多的事情
      • Python 使用缓存功能进行接口性能调优
      • 用pandas新建excel并设置表头
      • RBAC
      • Python读取文件去除回车
      • Python经过OpenSSL获取指定域名对应的SSL证书
      • Python爬取百度指数中的搜索指数
      • Python中反斜杠u类型(uXXXX)字符串转换为Unicode字符串
      • Python两种方式获取SSL证书信息
      • Python 项目环境变量方法
      • PYTHON 获得当前路径
      • Python 自动申请 SSL 证书
      • Python 拆分 URL
      • Python 谷歌令牌
      • Python redis 操作
      • Python 封装 Redis
      • dnspython实现NS记录查询
      • 2.7 版本 telegram 机器人
      • 最全的Python虚拟环境使用方法
      • hasattr、getattr和setattr的使用方法
      • 字符串与字节之间转换
      • 模块-文件共享-SimpleHTTPServer
      • 模块-文本变量-configparser
      • 模块-SSH连接-paramiko
      • HTTPS服务实现
      • 列表骚操作
      • PyMysql
      • 基础语法
      • 终端Print颜色
      • loguru日志库
      • 自动安装Nginx
      • Python3.7源码安装
      • linux 一键安装 conda
      • Pipenv常用的命令
      • 监听服务器的端口
      • 获取证书到期时间
      • 检测域名被墙污染
      • 发送电子邮件信息
      • 发送Telegram信息
      • 输出进度条的图形
      • Cloudflare DNS A记录自动更新脚本
      • Cloudflare-API操作
      • UUID库生成唯一ID
      • 静态方法、普通方法、类方法
      • 循环切片+多线程+消息队列queus
      • 注册 Forms
      • 循环切片+多线程+消息队列queus
      • Python 列表字符串转换整型
      • SQLAlchemy的查询分页和获取总条数
      • 使用shell和python分别实现简单菜单功能
      • 获取checkbox选中状态的两种方式
      • QQ爆红检测
      • 域名备案查询
      • 结合腾讯云发送短信
      • 爬虫神器PyQuery的使用方法
      • Dict 转换
      • 获取证书到期时间
      • 虚拟环境使用
      • 无限级分类的两种实现方式
      • 两个数组交集|并集|差集方法
      • https
      • ​统计代码耗时的几种方法
      • datetime库常用转换
      • datatime库计算当前时间||其他时间运算
      • 监控网站可用性并发送Telegram通知
      • 监控SSL证书可用性并发送Telegram通知
      • 监控端口可用性并发送Telegram通知
      • 自动下载阿里云OSS桶文件
      • 自动上传文件到阿里云OSS
      • 获取cpu,根据cpu数量分配线程数
      • 获取自己的公网IP地址
      • Pyhton检测邮箱是否可用
      • Python使用代理检测域名状态
    • Flask
      • Nginx 业务接口触发拉黑IP
      • 结合uWSGI和Nginx部署flask项目
      • pip错误
      • Flask请求扩展与中间件
      • Flask拦截器
      • Flask-SQLAlchemy批量插入数据性能测试
      • Flask-CeleryExt
      • Flask 级联删除
      • Flask-SQLAlchemy详解
      • Flask + Celery + Redis 异步发送邮件
      • Flask http异常捕获
      • Flask 自定义命令 类似于django的manage.py
      • Flask 项目中解决csrf攻击
      • Flask 视图请求钩子函数
      • 一、Pipenv管理项目
      • 二、摸版
      • 三、处理文件上传
      • 四、 Flask 定时任务
      • 五、REST架构API方法
      • 六、搭建查询IP地址接口
      • 七、Flask+Github触发webhoop接口
      • Flask用paginate实现数据分页
      • Flask 文件流下载
    • Django
      • Djanog admin 有用的设置
      • Django 下 redis 操作
      • Django Ninja
      • Django django-import-export
      • Django Admin自动填充当前用户的示例代码
      • 在Django Admin中为list_filter创建自定义过滤器
      • 1、Django基础一
      • 2、Django基础二
      • 3、Django后台基础用法
      • 4、Django缓存
      • 5、Django日志
      • 6、Django设置csrf_token
      • 7、Django图片上传前端显示
      • 8、Django全文搜索
      • 9、Django Queryset转Json
      • 10、Django开发||生产环境
      • 11、Django邮箱||验证码||登录
      • 12、Django解决扩展用户表时,后台ADMIN显示密码为明文的问题
      • 13、ORM批量添加||更新数据
      • 14、Django分页并前端显示
      • 15、Celery异步任务集成
      • 16、Django获取访问IP地址
      • 17、Django重定向返回上一页
      • 18、Django自定义页面跳转链接
      • 19、利用 django-simpleui 模板在管理后台添加自定义的菜单和自定义的页面、设置访问权限
      • 20、Django导入导出功能
      • 1000、Django错误
      • 21、Django3实现Websocket最简单demo
      • 22、打包django项目成exe文件
      • Vue+websocket+django实现WebSSH demo
      • 24、related_namerelated_query_name 的区别
    • DRF
      • permissions.py源码分析
      • DRF接口 + Vue实现下载文件
      • DRF基础笔记
      • API跨域设置
      • JWT多方式登录及自定义验证
    • Fastapi
      • 运维自动化之域名系统
      • 自定义异常
      • fastapi tortoise-orm 使用一
      • fastapi tortoise-orm 使用二
      • fastapi tortoise-orm 使用三
      • fastapi处理tortoise-orm异常
      • 基于FastAPI和Tortoise-ORM的WebSocket类的封装
      • FastAPI中使用调度程序执行异步任务,与Redis交互
      • Sqlalchemy异步操作
      • 第一个Fastapi
      • FastAPI 中间件
      • FastApi APIRouter引用
      • FastAPI 依赖注入
      • FastAPI 响应体模型及校验
      • FastAPI 项目结构优化
      • FastAPI 文件上传
      • FastAPI 数据库一对一
      • FastAPI 数据库一对多
      • FastAPI 数据库多对多
      • FastAPI 数据库创建
      • FastAPI 内部调用路径
      • FastAPI 请求参数及校验
      • FastAPI 请求模型及校验
      • FastAPI 内部调用路径
      • FastAPI 路径参数及校验
      • FastAPI 路径、请求、请求体综合使用
      • FastAPI 类视图
      • FastAPI 静态文件
      • FastAPI 接口文档配置相关
      • FastAPI 后台任务
      • FastAPI 更新数据
      • FastAPI 根据环境不同连接不同数据库
      • FastAPI 封装接口返回
      • FastAPI 日志
      • FastAPI 封装分页
      • FastAPI 端点调试
      • FastAPI 定制返回Response
      • FastAPI 操作数据库
      • FastAPI 部署 uvicorn + supervisor
      • FastAPI WebSocket
      • FastAPI startup 和 shutdown
      • FastAPI sql 转换 json
      • FastAPI Redis 操作
      • FastAPI OAuth2 认证
      • FastAPI Jwt 认证
      • FastAPI 表单使用
      • FastAPI Docker 方式
      • FastAPI CORS跨域
      • FastAPI Cookie 参数,Header参数
      • fastapi操作异步redis模块aioredis
      • RESTFUL API 访问和刷新令牌
    • PHP
      • thinkphp
        • 留言版系统
  • centos
    • Iptable
      • Firewalld使用ipset快速屏蔽指定国家的IP访问
      • Iptable 使用ipset设置防火墙端口白名单,只让指定国家访问
    • Minio
      • Minio 部署
      • Python 操作 minio
      • 挂载谷歌云盘
    • SSL
      • CentOS下自动申请、部署Let's Encrypt免费SSL证书教程(Nginx亲测)
    • Linux基操
      • 三次握手和四次挥手
      • Linux-性能常用命令
      • 常见DDOS攻击类型
      • Ubuntu配置IP及免密登录
      • Ubuntu 替换阿里云镜像
      • ntpdate 无法同步时间问题
      • linux下redis的使用
      • hey压测工具
      • Linux-Node安装
      • Linux-UFW设置
      • Linux-vsftp
      • Linux-小数点计算
      • Linux-内核升级
      • Linux-终端代理
      • Linux-输出字体颜色
      • Linux-SSH密钥登录
      • Linux-磁盘扩容缩容
      • Linux-设置时间时区
      • Linux-服务器入侵排查
      • Linux-增加Swap方法
      • Linux-vim可视化模式
      • Linux-Crontab定时任务
      • Linux-Supervisor进程管理
      • Linux-处理大量的 TIME_WAIT
      • awk|grep|sed|find
      • find常规用法
      • Linux-排查磁盘IO读写很高
      • Linux-排查CPU只用率很高
      • ubuntu搭建NFS服务
      • Centos7-yum问题
      • ubuntu 24.X 安装 python2.7
    • 科学上网
      • pptp
      • Sock5 代理
      • Goproxy 代理
      • Stunnel 加密 Squid 代理服务
      • MTProxy代理,专注Telegram
      • 使用一键脚本搭建L2TP+IPSec
    • Ansible
      • Ansible 部署 nginx
      • Ansible 部署 Supervisor
      • Ansible 基础笔记
      • 过滤器
      • ansible回调函数使用
      • 如何使用ansible来快速同步目录
      • Ansible 错误
      • Ansible 删除多个文件或目录
      • Ansible Api二次封装
      • Ansible 过滤器
      • Playbook 获取主机组主机数
      • Playbook 部署Squid代理
      • Playbook Debug用法
      • Playbook 部署Node_exporter
      • 批量安装Nginx
      • 安装K8S
      • Ansible如何通过跳板机连接目标机器
    • Git 教程
      • 首次使用
      • Git上传文件卡住无响应
      • Git速查表
      • Git 安装&配置
      • Git 创建仓库
      • Git 基本操作
      • Git 分支管理
      • Git 服务器搭建
      • Git基操-tag
      • Git修改远程仓库地址
      • Git clone速度太慢怎么办
      • Git 修改 tag 内容如何操作
      • Git 大文件上传
      • Github 创建新分支
    • Docker
      • containerd 安装
      • docker 镜像瘦身工具 docker-slim
      • Docker 与 IPtables
      • Page
      • Docker几种安装方式
      • Docker国内镜像加速
      • Docker容器自动启动设置
      • Docker使用Harbor无SSL
      • Docker使用Harbor的API
      • Dockerfile打包镜像优化
      • Docker自定义镜像查看日志方法
      • Dockerfile和docker-compose.yml设置时区
      • Swarm
        • 微服务架构部署问题
        • Swarm 基础命令
        • Swarm 安装使用
        • Swarm 服务版本更新|回滚
      • Docker-compose
        • Docker 镜像自动化部署
        • Prometheus+Grafana监控平台+钉钉报警
        • 基于Alpine构建php7+nginx(2)
        • 基于Alpine构建php7+nginx(1)
        • docker-compose mysql+django
        • docker-compose安装
        • docker compose升级
        • seata单节点或集群
        • 测试常用中间件快速启动
        • 开源监控-hertzbeat
      • Alpine构建镜像
        • 构建java镜像
        • Alpine-Timezone
        • Alpine-Redis
        • Alpine-Python
        • Alpine-Php
        • Alpine-Nginx
        • Alpine-Nginx-定制nginx
        • Alpine-Mysql
      • Page 3
      • Page 2
    • Shell
      • 批量远程执行命令脚本
      • Linux健康检查脚本
      • Page 1
      • 一键生成ssl自签名证书
      • 服务器日常巡检脚本
      • 生成100个随机6位数字
      • 9个实用 shell 脚本
      • 21 个非常实用的 Shell 拿来就用脚本实例
      • shell每秒执行一次
      • Shell脚本自动生成开头注释简介
      • Shell中$#、$0、set等的含义
      • Shell脚本书写规范
      • shell脚本里的变量怎么加一
      • Shell获取当前目录和上级目录
      • Nginx日志切割脚本(按天切割)
      • Redis源码安装脚本
      • Php源码安装脚本
      • Nginx 1.23.3 源码安装
      • Nginx 1.27.1 源码安装
      • MYSQL5.X源码安装脚本
      • Redis源码安装脚本
      • Lnmp各源码安装脚本
      • Linux打印系统配置信息脚本
      • expect交互
      • CentOS系统初始化脚本(适合CentOS 6.X和CentOS 7.X系列)
      • Ubuntu 系统初始化
      • Bash数组
      • 一键测试脚本bench.sh
      • 批量添加用户
      • Ftp-Python上传下载案例
      • Ftp-Mysql数据库全量自动备份删除7天前备份
      • Ftp-Mysql数据库的全量备份和增量备份脚本实例
      • Ftp-Python服务器
      • Shell脚本常用示例
      • Shell多进程模式
      • 管理Firewall防火墙脚本
      • MySQL5.7~8热备份
      • postgresql 库备份
    • Nginx
      • 一文搞定Nginx的压缩、黑白名单、防盗链、零拷贝、跨域、双机热备等知识
      • nginx 一把梭
      • 阿里云ESC的Nginx代理OSS
      • yum命令安装mariadb
      • Tengine安装lua
      • Nginx配置中的if判断
      • Nginx内置变量
      • nginx+php限制某个IP访问
      • Nginx 变量 set 使用方法
      • Nginx 判断值是否为空 设置变量 获取参数
      • lua随机值
      • 利用客户端随机跳转
      • JS代码简单的防封、防屏蔽、防举报、防红页面
      • Ngx_lua
      • WFT
      • 免费申请HTTPS六大方法
      • 502错误
      • Ngx基操
      • Ngx 配置文件实例
      • Ngx跨域解决方法
      • Ngx服务器内核优化
      • Ngx从安装到高可用
      • Ngx反向代理支持WSS
      • Ngx配置用户名密码访问
      • Ngx配置Http(s)|WS|WSS
      • Ngx算法|Rewrite规则|优先级
      • Ngx中websocket服务的配置
      • mp4
      • 跨域设置
      • Ngx 第三方库 ngx_brotli
      • Ngx 反向代理缓存规则
      • Ngx 反向代理禁用缓存
      • Logrotate实现nginx日志切割
    • Tomcat
      • nginx 前端https +tomcat 后端 http 非80、443端口反向代理的配置方式
      • Tomcat 8.x基于Redis Session会话保持
    • Keepalived
      • 高可用--Nginx+keepalived
      • 高可用-Haproxy+keepalived
      • 高可用-Lvs+Keepalived
    • Mysql
      • yum命令安装mariadb
      • ubuntu 首次安装mysql修改密码
      • 1、Yum安装MySql
      • 2、源码安装MYSQL5.7.21
      • 3、MYSQL主从冷备
      • 4、MYSQL主主热备
      • 5、Xtrabackup全备增备
      • 6、MYSQL管理员密码修改
      • 7、MYSQL字符集设置
      • 8、MYSQL命令整理
      • 9、MySQL数据导出csv格式
      • 10、MySQL根据日期查询数据的sql语句
      • 11、如何优雅备份MySQL?
      • 12、如何在已有的数据库中无损主主备份?
      • 13、PXC集群
      • 14、TIUP TIDB
      • 15、MySQL8.0锁情况排查
      • MYSQL 配置文件常用配置
      • Mysql 错误报错解决方法
      • 记录生产事故数据库被删
      • 压测 SQL 工具
    • Redis
      • Redis基操
      • Redis-cluster监控部署方案
    • Php
      • PHP项目迁移部署错误
      • 查找linux下进程占用CPU过高的原因,以php-fpm为例
    • Vscode
      • vscode实现远程linux服务器上Python开发
    • Prometheus
      • 使用 TLS 加密 Prometheus API 和 UI 端点
      • 使用基本身份验证保护 Prometheus API 和 UI 端点
      • 黑盒
      • prometheus
      • node-exporter https认证
      • 中文资料地址
      • 告警-微信
      • 告警-钉钉
      • 监控-基础指标
      • 监控-自定义指标
      • 黑盒-blackbox_exporter
      • 监控-平台搭建+邮件报警
      • Prometheus 监控 Redis
      • Prometheus 监控 NGINX
      • Prometheus 监控进程
      • PushGateway 数据上报采集
      • Prometheus 将数据远程写入 InfluxDB 存储
      • 外部Prometheus监控k8s集群资源
      • prometheus-Agent服务注册
      • Prometheus-自动发现监控 AWS EC2
      • Prometheus-黑盒blackbox
      • Prometheus-Pushgateway自定义
      • Prometheus-采集MySQL指标
      • Prometheus-采集Redis指标
      • Prometheus-采集Kafka指标
    • Vue
      • Vite解决开发、生产服务器的自动切换
      • js实现60秒倒计时
      • H5页面实现下载文件
      • loading加载动画
      • Vue如何新建一个项目
      • Vue开发菜单权限与按钮权限管理
      • Vue 错误
      • Vue开发必备插件
      • Vue如何新建一个项目
      • vue-router+nginx 非根路径配置方法
      • vue中配置proxy指定api请求地址
      • vue开发----关于字符串去除空格的方法
      • vue表格中动态更新,动态删除,动态添加
      • 项目
        • 1 01.创建 Vite项目并安装 Vscode 插件
        • 1 02.引入ElementPlus和基本使用
        • 1 03.引入windicss工具库和配置,安装代码提示
        • 1 04.引入vue router4路由配置和404页面捕获
        • 1 05.登录页图标引入响应式开发
        • 1 06.结合@apply实现样式抽离
        • 1 07.登录表单验证处理
        • 1 08.引入axios请求库和登录接口交互
        • 1 09.引入cookie存储用户token
        • 1 10.封装请求拦截器和响应拦截器及工具库
        • 1 11.引入vuex状态管理用户信息
        • 1 12.全局路由拦截实现登录判断
        • 1 13.登录功能完善
        • 1 14.退出功能实现
        • 2 01.全局loading进度条实现
        • 2 02.动态页面标题实现
        • 2 03.后台主布局实现
        • 2 04.公共头部开发 样式布局
        • 2 05.公共头部开发 刷新和全屏
        • 2 06.公共头部开发 修改密码
        • 2 07.封装通用弹框表单组件
        • 2 08.封装组合式api简化代码
        • 2 09.侧边菜单开发 样式布局和路由跳转
        • 2 10.展开和收起菜单功能实现
        • 2 11.菜单选中和路由关联
        • 2 12.根据菜单动态添加路由
        • 2 13.封装标签导航组件实现
        • 页面缓存实现
        • 2 15.transition全局过渡动画
        • 2 16.统计面板组件开发
        • 2 17.数字滚动动画实现
        • 2 18.分类组件开发和跳转
        • 2 19.echarts图表组件开发和交互
        • 2 20.店铺和交易提示组件开发和交互
        • 2 21.v permission指令按钮级权限控制
        • 2 22.封装上传多图组件功能实现
        • 2 23.公告栏模块Curd操作
        • 2 24.封装组合式API特性 列表分页搜索增删改
        • 2 25.封装自定义下拉图标组件
    • Bootstrap
      • jQuery判断数组中是否存在某个值的方法
      • jQuery 判断数组中是否包含某个值
      • jQuery checkbox选中和不选中的值_设置checkbox选中状态
      • BootStrap中关于Select下拉框选择触发事件
    • Zabbix
      • Zabbix_Agent
      • PY发送钉钉通知
      • 部署zabbix脚本
      • SHELL发送邮件
      • NGINX状态监控
      • Zabbix模板
    • CICD
      • Pipeline
        • Docker 容器服务重启
        • Docker 镜像打包发布回滚
      • Jenkins+Ansible-playbook自动发布回滚
      • Jenkins、GitLab部署
      • Docker+Nginx+Jenkins+GitLab实现前端自动化部署
      • Jenkins 版本回滚
      • Jenkins 自动化
      • GitLab
        • GitLab 安装
        • GitLab 不同版本迁移
        • GitLab 如何设置中文
    • Email 自建
      • mailcow
      • iRedMail 更改 Mail 域
      • poste.io
  • 消息队列
    • 消息队列选型
  • ES
    • 监控输出到ES错误日志告警
    • filebeat收集java日志
    • filebeat 快速安装
    • ELK配置之,filebeat更改自定义索引名称
    • ELK-Kafka-Filebeat
    • Docker 部署 3 节点 ES 集群
    • ElasticSearch Python操作
    • ElasticSearch常规操作
    • ElasticSearch 7.7.0(单机版)+ Ik 分词器 + ES-head 可视化插件
    • ES 常见错误
    • Grafana+ES+Nginx
    • ES-自动删除7天前索引日志
  • Ubuntu
    • Linux时间与系统时间相差8小时的解决办法
    • Ubuntu 重启网卡的三种方法
    • Ubuntu 网卡配置为静态方法
  • Java
  • 😘Kubernetes
    • k3s
      • K3s集群安装
      • Longhorn 分布式存储
      • kubesphere 管理界面
    • 常用 YAML 模板
      • external-mysql
      • cluster-nacos
      • cluster-xxl-job-admin
      • cluster-seata
      • singlenode-es
      • singlenode-mysql
      • storage-mysql
      • singlenode-rabbitmq
      • singlenode-redis
      • singlenode-sentinel
    • Rancher
      • Rke集群
      • RKE1.5.7安装集群
    • Etcd
      • Docker-单节点单Etcd部署
      • Docker-单节点多Etcd部署
      • Docker-多节点Etcd部署
      • CronJob资源控制器进行定时备份
      • 生产环境ETCD高可用集群
    • Ceph
      • Docker-单节点,多OSD集群
      • Ansible-ceph集群
      • Docker-ceph集群
    • k8s
      • kubernetes 1.23.6
      • kubesphere
      • 使用kubeadm搭建高可用的K8s集群
      • 使用kubeadm快速部署一个K8s集群
      • Rancher+k3s
      • k3s执行helm命令报错Error Kubernetes cluster unreachable Get “httplocalhost8080versiontimeout=32s“
      • 尚硅谷k8s课堂随笔
      • kubernetes学习随笔
      • 问题
      • k8s清除环境脚本
      • Minikube单机版k8s实验环境
      • 常用中间件快速启动
      • kubeadm单机版k8s测试部署
      • kubeadm生产环节高可用部署
      • 跨VPC网络K8S
        • k8s无法删除namespace
        • 跨VPC网络-工具安装
        • 跨VPC网络-二进制ETCD集群
        • 跨VPC网络-使用 kubeadm 创建集群(v1.24)
        • 部署 metrics-server
      • K8s证书考试
    • Helm
      • Helm 安装 MongoDB-分片集群
      • Helm 安装 MongoDB-副本集群
      • helm 安装 rocketmq
      • helm 安装 MongoDB 集群
      • Helm 安装 Redis 集群
      • Helm 安装 Redis 哨兵主从高可用
      • Helm 安装
      • Helm安装Kafka
      • Helm同时部署多个域名
      • Helm内置对象和摸版语言
      • 如何使用github作为Helm的chart仓库
      • Helm 安装 Kubernetes 监控套件
    • 错误记录
      • kubelet启动报错
  • Go
    • 学习笔记
      • 1、Go环境安装
      • 2、Go目录结构及包管理
      • 3、Go的编译和运行
      • Gin 基础
      • Gin 项目实战
      • Go 基础
      • Gorm 基础
      • Go中&与的区别以及使用
      • myblog
    • 视频切片
    • 面试
      • 灵魂拷问
      • 面试稳了
      • 自己经历的面试问题总结
      • K线、均线、趋势、形态、N型反转
    • 错误
    • 小工具
      • 其他工具列表
      • 性能压力测试小工具 wrk
    • AWS
      • CDN缓存刷新
Powered by GitBook
On this page
  • 1. RESTful API规范
  • 1.1 域名(domain)
  • 1.2 版本(version)
  • 1.3 路径(Endpoint)
  • 1.4 HTTP动词
  • 1.5 过滤信息(Filtering)
  • 1.6 状态码(status Codes)
  • 1.7 错误处理(Error handling)
  • 1.8 返回结果
  • 1.9 超媒体(Hypermedia API)
  • 1.10 其他
  • 2. Django Rest_Framework简介与安装
  • 2.1 Django Rest_Framework简介
  • 2.2 环境安装与配置
  • 3. DRF创建项目流程
  • 3.1 添加rest_framework应用
  • 3.2 创建模型操作类
  • 3.3 创建序列化器
  • 3.4 写视图
  • 3.5 定义路由
  • 3.6 运行测试
  • 4. 序列化器–Serializer
  • 4.1 Serializer构造方法
  • 4.2 序列化功能的使用
  • 4.3 反序列化功能的使用
  • 4.4 反序列化功能校验顺序
  • 4.5 序列化器一些常用参数的说明
  • 4.6 反序列化的数据保存功能
  • 4.7 反序列化的数据更新功能
  • 4.8 Field字段参数:read_only和write_only
  • 4.9 ModelSerializer的使用(推荐)
  • 5. 视图
  • 5.1 request对象和response对象
  • 5.2 基于 APIView 视图
  • 5.3 基于 GenericAPIView 视图
  • 5.4 基于Mixin个视图扩展类视图
  • 5.5 基于GenericAPIView的视图子类 APIView
  • 5.6 ViewSet
  • 5.7 ViewSet + APIView
  • 5.8 GenericViewSet + Mixin
  • 5.9 以上方法改良后
  • 5.10 基于ModelViewSet视图(终极版)
  • 5.11 ReadOnlyViewset(不常用)
  • 5.12 添加自定义动作(action装饰器)
  • 6. 路由:Routers
  • 6.1 如何添加路由数据
  • 6.2 在视图集中附加action声明
  • 6.3 DefaultRouter和SimpleRouter的区别(了解)
  • 7. 认证/权限/限流/过滤/排序/分页/异常处理
  • 7.1 认证 Authentication
  • 7.2 自定义认证
  • 7.3 权限:Permissions
  • 7.4 自定义权限
  • 7.5 限流:Throttling
  • 7.6 可选择的限流类
  • 7.7 过滤:Filtering
  • 7.8 排序
  • 7.9 排序 + 过滤
  • 7.10 分页:Pagination
  • 7.11 异常处理:Exception
  • 8. 自动生成接口文档
  • 8.1 安装依赖
  • 8.2 配置路由
  • 8.3 配置setting文件
  • 8.4 文档描述说明的定义位置
  1. PYTHON
  2. DRF

DRF基础笔记

1. RESTful API规范

1.1 域名(domain)

# 应该尽量将API部署在专用域名之下。
https://api.example.com
https://www.example.com

1.2 版本(version)

# 方法一: 版本号放入 URL
http://api.example.com/app/1.0/foo
http://api.example.com/app/2.0/foo
http://api.example.com/app/3.0/foo

# 方法二: 版本号放入 HTTP 头信息中

Accept: version=1.0
Accept: version=2.0
Accept: version=3.0

1.3 路径(Endpoint)

路径又称"终点"(endpoint),表示API的具体网址,每个网址代表一种资源(resource) 对于一个简洁结构,你应该始终用名词。 此外,利用的HTTP方法可以分离网址中的资源名称的操作。

对于一个简洁结构,你应该始终用名词。 此外,利用的HTTP方法可以分离网址中的资源名称的操作。

GET /products    :将返回所有产品清单
POST /products   :将产品新建到集合
GET /products/4  :将获取产品 4
PATCH(或)PUT /products/4 :将更新产品

1.4 HTTP动词

对于资源的具体操作类型,由HTTP动词表示。

常用的HTTP动词有下面四个(括号里是对应的SQL命令)。

  • GET(SELECT):从服务器取出资源(一项或多项)。

  • POST(CREATE):在服务器新建一个资源。

  • PUT(UPDATE):在服务器更新资源(客户端提供改变后的完整资源)。

  • DELETE(DELETE):从服务器删除资源。

还有三个不常用的HTTP动词。

  • PATCH(UPDATE):在服务器更新(更新)资源(客户端提供改变的属性)。

  • HEAD:获取资源的元数据。

  • OPTIONS:获取信息,关于资源的哪些属性是客户端可以改变的。 下面是一些例子。

GET /zoos:列出所有动物园
POST /zoos:新建一个动物园(上传文件)
GET /zoos/ID:获取某个指定动物园的信息
PUT /zoos/ID:更新某个指定动物园的信息(提供该动物园的全部信息)
PATCH /zoos/ID:更新某个指定动物园的信息(提供该动物园的部分信息)
DELETE /zoos/ID:删除某个动物园
GET /zoos/ID/animals:列出某个指定动物园的所有动物
DELETE /zoos/ID/animals/ID:删除某个指定动物园的指定动物

1.5 过滤信息(Filtering)

如果记录数量很多,服务器不可能都将它们返回给用户。API应该提供参数,过滤返回结果。 下面是一些常见的参数。query_string 查询字符串,地址栏后面问号后面的数据,格式: name=xx&sss=xxx

?limit=10               :指定返回记录的数量
?offset=10              :指定返回记录的开始位置。
?page=2&per_page=100    :指定第几页,以及每页的记录数。
?sortby=name&order=asc  :指定返回结果按照哪个属性排序,以及排序顺序。
?animal_type_id=1       :指定筛选条件

1.6 状态码(status Codes)

服务器向用户返回的状态码和提示信息,常见的有以下一些(方括号中是该状态码对应的HTTP动词)。

200 OK - [GET]                   :服务器成功返回用户请求的数据
201 CREATED - [POST/PUT/PATCH]   :用户新建或修改数据成功。
202 Accepted - [*]               :表示一个请求已经进入后台排队(异步任务)
204 NO CONTENT - [DELETE]        :用户删除数据成功。
400 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作
401 Unauthorized - [*]           :表示用户没有权限(令牌、用户名、密码错误)。
403 Forbidden - [*]               表示用户得到授权(与401错误相对),但是访问是被禁止的。
404 NOT FOUND - [*]              :用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。
406 Not Acceptable - [GET]       :用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。
410 Gone -[GET]                  :用户请求的资源被永久删除,且不会再得到的。
422 Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误。
500 INTERNAL SERVER ERROR - [*]  :服务器发生错误,用户将无法判断发出的请求是否成功。

状态码的完全列表参见这里或这里。 http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html

1.7 错误处理(Error handling)

如果状态码是4xx,服务器就应该向用户返回出错信息。一般来说,返回的信息中将error作为键名,出错信息作为键值即可。

{
    error: "Invalid API key"
}

1.8 返回结果

针对不同操作,服务器向用户返回的结果应该符合以下规范。

GET /collections:返回资源对象的列表(数组)
GET /collection/ID:返回单个资源对象(json)
POST /collection:返回新生成的资源对象(json)
PUT /collection/ID:返回完整的资源对象(json)
DELETE /collection/ID:返回一个空文档(空字符串)

1.9 超媒体(Hypermedia API)

RESTful API最好做到Hypermedia(即返回结果中提供链接,连向其他API方法),使得用户不查文档,也知道下一步应该做什么。

比如,Github的API就是这种设计,访问api.github.com会得到一个所有可用API的网址列表。

{
"current_user_url": "https://api.github.com/user",
"authorizations_url": "https://api.github.com/authorizations",
// ...
}

从上面可以看到,如果想获取当前用户的信息,应该去访问api.github.com/user,然后就得到了下面结果。

{
  "message": "Requires authentication",
  "documentation_url": "https://developer.github.com/v3"
}

上面代码表示,服务器给出了提示信息,以及文档的网址。

1.10 其他

服务器返回的数据格式,应该尽量使用JSON,避免使用XML

2. Django Rest_Framework简介与安装

2.1 Django Rest_Framework简介

核心思想: 缩减编写api接口的代 – DRF

Django REST framework是一个建立在Django基础之上的Web 应用开发框架,可以快速的开发REST API接口应用。在REST framework中,提供了序列化器Serialzier的定义,可以帮助我们简化序列化与反序列化的过程,不仅如此,还提供丰富的类视图、扩展类、视图集来简化视图的编写工作。REST framework还提供了认证、权限、限流、过滤、分页、接口文档等功能支持。REST framework提供了一个API 的Web可视化界面来方便查看测试接口。

  • 中文文档:https://q1mi.github.io/Django-REST-framework-documentation/#django-rest-framework

  • github: https://github.com/encode/django-rest-framework/tree/master

  • 英文文档:https://www.django-rest-framework.org/

特点:

提供了定义序列化器Serializer的方法,可以快速根据 Django ORM 或者其它库自动序列化/反序列化;
提供了丰富的类视图、Mixin扩展类,简化视图的编写;
丰富的定制层级:函数视图、类视图、视图集合到自动生成 API,满足各种需要;
多种身份认证和权限认证方式的支持;[jwt]
内置了限流系统;
直观的 API web 界面;
可扩展性,插件丰富

2.2 环境安装与配置

DRF需要以下依赖:

  • Python (2.7, 3.2, 3.3, 3.4, 3.5, 3.6)

  • Django (1.10, 1.11, 2.0)

安装 DRF

pip install django==2.2
pip install djangorestframework

3. DRF创建项目流程

3.1 添加rest_framework应用

在settings.py的INSTALLED_APPS中添加’rest_framework’。

INSTALLED_APPS = [
    ...
    'rest_framework',
]

项目中如果使用rest_framework框架实现API接口,主要有以下三个步骤:

  • 将请求的数据(如JSON格式)转换为模型类对象

  • 操作数据库

  • 将模型类对象转换为响应的数据(如JSON格式)

3.2 创建模型操作类

class Student(models.Model):
    # 模型字段
    name = models.CharField(max_length=100,verbose_name="姓名",help_text='提示文本:不能为空')
    sex = models.BooleanField(default=1,verbose_name="性别")
    age = models.IntegerField(verbose_name="年龄")
    class_null = models.CharField(max_length=5,verbose_name="班级编号")
    description = models.TextField(max_length=1000,verbose_name="个性签名")

    class Meta:
        db_table="tb_student"
        verbose_name = "学生"
        verbose_name_plural = verbose_name

创建数据库(嫌麻烦,可以使用默认的sqlite3测试)

create database students charset utf8;

主引用中__init__.py设置使用pymysql作为数据库驱动

import pymysql
pymysql.install_as_MySQLdb()

settings.py配置文件中设置mysql的账号密码

DATABASES = {
    # 'default': {
    #     'ENGINE': 'django.db.backends.sqlite3',
    #     'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    # },
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': "students",
        "HOST": "127.0.0.1",
        "PORT": 3306,
        "USER": "root",
        "PASSWORD":"123456",
    }
}

终端下,执行数据迁移

python manage.py makemigrations
python manage.py migrate

3.3 创建序列化器

在students应用目录中新建 serializers.py 用于保存该应用的序列化器。 创建一个 StudentModelSerializer 用于序列化与反序列化。

from rest_framework import serializers
from .models import Student

# 创建序列化器类,回头会在试图中被调用
class StudentSerializer(serializers.ModelSerializer):
    class Meta:
        model = Student
        fields = "__all__"
  • model 指明该序列化器处理的数据字段从模型类Student参考生成

  • fields 指明该序列化器包含模型类中的哪些字段,'all’指明包含所有字段

3.4 写视图

在students应用的views.py中创建视图StudentViewSet,这是一个视图集合

from rest_framework.viewsets import ModelViewSet
from .models import Student
from .serializers import StudentSerializer

# Create your views here.
class StudentViewSet(ModelViewSet):
    queryset = Student.objects.all() # 指明该视图集在查询数据时使用的查询集
    serializer_class = StudentSerializer # 指明该视图在进行序列化或反序列化时使用的序列化器
  • queryset 指明该视图集在查询数据时使用的查询集

  • serializer_class 指明该视图在进行序列化或反序列化时使用的序列化器

3.5 定义路由

在students应用的urls.py中定义路由信息。

from . import views
from rest_framework.routers import DefaultRouter

# 路由列表
urlpatterns = []
router = DefaultRouter()  # 可以处理视图的路由器,自动通过视图来生成增删改查的url路径
router.register('students', views.StudentViewSet)  #students是生成的url前缀,名称随便写, 向路由器中注册视图集

urlpatterns += router.urls  # 将路由器中的所以路由信息追到到django的路由列表中
                            
# urlpatterns = [
#     re_path('^', include(router.urls)),
#     re_path(r'^api-auth/', include('rest_framework.urls',
#             namespace='rest_framework')),
# ]

最后把students子应用中的路由文件加载到总路由文件中.

from django.contrib import admin
from django.urls import path,include  #path:直接写路径,re_path:可以写正则匹配

urlpatterns = [
    path('admin/', admin.site.urls),
    path("api/",include("students.urls")),
]

3.6 运行测试

运行该django项目

在浏览器输入路径,可以看到DRF提供的API Web浏览页面 在页面底部可以提交post请求,添加到数据库 在网址中输入路径/1,可以访问id为1的信息 在网址中输入路径/S/,可以访问删除的接口

4. 序列化器–Serializer

作用:

  1. 序列化,序列化器会把模型对象转换成字典,经过response以后变成json字符串

  2. 反序列化,把客户端发送过来的数据,经过request以后变成字典,序列化器可以把字典转成模型

  3. 反序列化,完成数据校验功能

定义好Serializer类后,就可以创建Serializer对象了。

4.1 Serializer构造方法

Serializer(instance=None, data=empty, **kwarg)

说明:

1)用于序列化时,将模型类对象传入instance参数

2)用于反序列化时,将要被反序列化的数据传入data参数

3)除了instance和data参数外,在构造Serializer对象时,还可通过context参数额外添加数据,如

serializer = AccountSerializer(account, context={'request': request})

通过context参数附加的数据,可以通过Serializer对象的context属性获取。

  1. 使用序列化器的时候一定要注意,序列化器声明了以后,不会自动执行,需要我们在视图中进行调用才可以。

  2. 序列化器无法直接接收数据,需要我们在视图中创建序列化器对象时把使用的数据传递过来。

  3. 序列化器的字段声明类似于我们前面使用过的表单系统。

  4. 开发restful api时,序列化器会帮我们把模型数据转换成字典.

  5. drf提供的视图会帮我们把字典转换成json,或者把客户端发送过来的数据转换字典.

4.2 序列化功能的使用

1 定义一个序列化器,在应用中创建一个py文件,serializers.py

from rest_framework import serializers

class StudentSerizlizer(serializers.Serializer):
    name = serializers.CharField()
    age = serializers.IntegerField()
    class_null = serializers.CharField()
    description = serializers.CharField()

2 写views.py文件

from django.http import JsonResponse
from django.views import View
from .models import Student
from .serializers import StudentSerizlizer

# Create your views here.

class StudentViewSet(View):
    def get(self, request):
        # 查询所有数据
        all = Student.objects.all() 
        
        # 查询一条数据
        one = Student.objects.get(id=1)  
        
        # 多条记录序列化必须加many=True参数
        '''serializer.data是列表里有多个小字典的形式'''
        serializer = StudentSerizlizer(instance=all,many=True)
        
        # 单条记录序列化
        '''serializer.data是一个字典'''
        serializer = StudentSerizlizer(instance=one)  #得到的结果为字典 data


        # 若要序列化非dict对象,需要添加safe=false参数
         # 若要显示中文,需要添加ensure_ascii选项
        return JsonResponse(serializer.data,safe=False,json_dumps_params={'ensure_ascii':False})

3 配置路由

from django.urls import path
from student import views

urlpatterns = [
    path('students/', views.StudentView.as_view())
]
from django.contrib import admin
from django.urls import path, include #path:直接写路径,re_path:可以写正则匹配

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/', include('student.urls')),
]

测试结果:

4.3 反序列化功能的使用

1 定义一个序列化器,在当前应用中新建serializers.py

from rest_framework import serializers

# 自定义校验函数
def check666(val):
    if '666' in val:
        raise serializers.ValidationError('不能光喊6666啊,没有用')
    else:
        return val


class StudentSerializer(serializers.Serializer):
    # validatior代表检查name时,会通过一遍check666函数
    name = serializers.CharField(max_length=4, validators=[check666, ])
    age = serializers.IntegerField(max_value=18)
    class_null = serializers.CharField()

    # required=False,allow_null=True允许字段为空,也就是不用传递过来这个data
    description = serializers.CharField(required=False, allow_null=True)

    # allow_blank=True 允许只为空字符串
    # description = serializers.CharField(allow_blank=True)

    # 局部钩子 validate_字段名:针对单个字段进行校验
    def validate_name(self, val):
        if '777' in val:
            raise serializers.ValidationError('错误!!')
        return val

    # 全局钩子 validate : 针对多个属性数据进行校验
    def validate(self, data):
        print(data)
        age = data.get('age')
        class_null = data.get('class_null')
        if age == class_null:
            raise serializers.ValidationError('错误!!!!')
        return data

2 写views.py文件

结合 全局钩子和局部钩子 验证

from django.http import JsonResponse
from django.views import View
from .serializers import StudentSerializers
from django.views.decorators.csrf import csrf_exempt

# Create your views here.
class StuView(View):
  def get(self, request):
    ''''''

    def post(self, request):
        recv_data = {
            'name': request.POST.get('name'),
            'age': request.POST.get('age'),
            'class_null': request.POST.get('class_null'),
            'description': request.POST.get('description'),
        }

        # 接收传过来的数据,反序列化校验,全部通过得到True
        ser = StudentSerializer(data=recv_data)
        if ser.is_valid():
            print(ser.validated_data)  # 打印正确信息

            # 写入数据库
            ret = Student.objects.create(**ser.validated_data)

            # 序列化,并返回新增的数据
            serializer = StudentSerializer(instance=ret)
            return JsonResponse(serializer.data, safe=False, json_dumps_params={'ensure_ascii': False})

        #  校验失败,返回错误信息
        error = ser.errors  #
        return JsonResponse(data={'msg': error})

    @csrf_exempt  # 取消 csrf token,测试环境中设置取消
    def dispatch(self, *args, **kwargs):
        return super(StudentViewSet, self).dispatch(*args, **kwargs)


3 url:

from django.urls import path
from use_unserializers import views
urlpatterns = [
    path('students/',views.StuView.as_view())

Tip: is_valid中的raise_exception参数

serializer.is_valid(raise_exception=True) # 等于True会主动抛出异常

4.4 反序列化功能校验顺序

执行顺序:

​  1.先执行name中CharField参数中的规则

  2.执行name的局部钩子函数

  3.执行age的IntegerField参数中的规则

  4.执行age的局部钩子函数

  5.执行全局钩子函数

4.5 序列化器一些常用参数的说明

常用字段:

字段
字段构造方式

BooleanField

BooleanField()

NullBooleanField

NullBooleanField()

CharField

CharField(max_length=None, min_length=None, allow_blank=False, trim_whitespace=True)

EmailField

EmailField(max_length=None, min_length=None, allow_blank=False)

RegexField

RegexField(regex, max_length=None, min_length=None, allow_blank=False)

SlugField

SlugField(maxlength=50, min_length=None, allow_blank=False) 正则字段,验证正则模式 [a-zA-Z0-9-]+

URLField

URLField(max_length=200, min_length=None, allow_blank=False)

UUIDField

UUIDField(format='hex_verbose') format: 1) 'hex_verbose' 如"5ce0e9a5-5ffa-654b-cee0-1238041fb31a" 2) 'hex' 如 "5ce0e9a55ffa654bcee01238041fb31a" 3)'int' - 如: "123456789012312313134124512351145145114" 4)'urn' 如: "urn:uuid:5ce0e9a5-5ffa-654b-cee0-1238041fb31a" 微软时间戳,通过微秒生成一个随机字符串

IPAddressField

IPAddressField(protocol='both', unpack_ipv4=False, **options)

IntegerField

IntegerField(max_value=None, min_value=None)

FloatField

FloatField(max_value=None, min_value=None)

DecimalField

DecimalField(max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None) max_digits: 最多位数 decimal_palces: 小数点位置

DateTimeField

DateTimeField(format=api_settings.DATETIME_FORMAT, input_formats=None)

DateField

DateField(format=api_settings.DATE_FORMAT, input_formats=None)

TimeField

TimeField(format=api_settings.TIME_FORMAT, input_formats=None)

DurationField

DurationField()

ChoiceField

ChoiceField(choices) choices与Django的用法相同

MultipleChoiceField

MultipleChoiceField(choices)

FileField

FileField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)

ImageField

ImageField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)

ListField

ListField(child=, min_length=None, max_length=None)

DictField

DictField(child=)

选项参数

参数名称
作用

max_length

最大长度

min_length

最小长度

allow_blank

是否允许为空

trim_whitespace

是否截断空白字符

max_value

最大值

min_value

最小值

通用参数

参数名称
说明

read_only

表明该字段仅用于序列化输出,默认False

write_only

表明该字段仅用于反序列化输入,默认False

required

表明该字段在反序列化时必须输入,默认True

default

反序列化时使用的默认值

allow_null

表明该字段是否允许传入None,默认False

validators

该字段使用的验证器

error_messages

包含错误编号与错误信息的字典

label

用于HTML展示API页面时,显示的字段名称

help_text

用于HTML展示API页面时,显示的字段帮助提示信息

4.6 反序列化的数据保存功能

方式一:直接在视图中保存

class StudentView(View):
    ''''''
    def post(self,request):
        recv_data = {
            'name': request.POST.get('name'),
            'age': request.POST.get('age'),
            'class_null': request.POST.get('class_null'),
            'description': request.POST.get('description'),
        }

        # 接收传过来的数据,反序列化校验,全部通过得到True
        ser = StudentSerializer(data=recv_data)
        if ser.is_valid():
            ret = models.Student.objects.create(
                **ser.validated_data
            )
            serializer = StudentSerizlizer(instance=ret)

            return JsonResponse(serializer.data,safe=False,json_dumps_params={'ensure_ascii':False})
        return JsonResponse(data={'msg': ser.errors})

方式二:视图中通过save方法来触发序列化器类中的create方法

 # 方式2 save+create

# 在序列化器中定义create方法来数据的保存
class StudentSerizlizer(serializers.Serializer):
    name = serializers.CharField(max_length=4,validators=[check666,])
    age = serializers.IntegerField(max_value=18)
    ......
    
    def create(self, validated_data): # 定义create方法
        '''self.validated_data和validated_data结果是相同的,所以直接用validated_data即可'''
        print(validated_data)
        ret = models.Student.objects.create(
            **validated_data
        )
        return ret

# 视图中通过save方法来触发序列化器类中的create方法
class StudentView(View):
    def post(self,request):
        '''略'''
        if ser.is_valid():
            '''通过save()方法去触发序列化器中的create方法'''
            instance = ser.save()  # 得到create方法的返回值
            serializer = StudentSerizlizer(instance=instance)

            return JsonResponse(serializer.data,safe=False,json_dumps_params={'ensure_ascii':False})

4.7 反序列化的数据更新功能

在序列化器中定义update方法来数据的更新

class StudentSerializer(serializers.Serializer):
    
    name = serializers.CharField(max_length=4, validators=[check666, ])
    age = serializers.IntegerField(max_value=18)
  '''''' 略

    # 添加更新动作
    def update(self, instance, validated_data):
        instance.name = validated_data['name']
        instance.age = validated_data['age']
        instance.class_null = validated_data['class_null']
        instance.description = validated_data['description']
        instance.save()
        return instance

视图部分

from django.http import QueryDict
class StudentViewSet(View):
  '''略'''
    def put(self, request):
        # 获取需要更新的数据
        put = QueryDict(request.body)
        put_str = list(put.items())[0][0]  # 将获取到的 Querydict对象转换成str类型
        put_dict = eval(put_str)           # 将str类型转换成字典
        recv_data = {
            'id': put_dict.get('id'),
            'name': put_dict.get('name'),
            'age': put_dict.get('age'),
            'class_null': put_dict.get('class_null'),
            'description': put_dict.get('description'),
        }

        obj = Student.objects.get(id=recv_data.get('id'))

        '''instance=obj用来指定这个save()触发的是序列化器中的update方法,而不是create方法,data就是你要更新的数据'''
        ser = StudentSerializer(instance=obj, data=recv_data)
        if ser.is_valid():
            ser.save()    # 触发序列化器类的update方法
            return JsonResponse(data={'msg': 'ok'})
        #  校验失败,返回错误信息
        error = ser.errors  #
        return JsonResponse(data={'msg': error})

Tip: 如果只更新部分字段

partial参数默认为False,若partial=True则代表只需要校验传入给序列化器的数据(data),其他的字段不校验.适用于部分数据更新

应用场景:

比如在序列化器中加入定义了五个字段。

如果用户在前端提交数据时,必须要将五个字段都提交,否则就会报错。

但是在现实应用场景下,有很多情况是不需要将五个字段全部提交的。比如数据更新

这个时候想要更改,但是序列化器的代码已经成型了,不太好进行修改。 【eg:提交需要提交5个字段,更新提交2个字段】

class StudentViewSet(View):
  '''略'''
    def put(self, request):
        # 获取需要更新的数据
        put = QueryDict(request.body)
        put_str = list(put.items())[0][0]  # 将获取到的 Querydict对象转换成str类型
        put_dict = eval(put_str)           # 将str类型转换成字典
        
        # 只传入需要更新的字段和必须传入的字段
        recv_data = {
            'name': put_dict.get('name'),                # 只传入校验必须的字段的
            'age': put_dict.get('age'),                  # 只传入校验必须的字段的
            'description': put_dict.get('description'),
        }

        obj = Student.objects.get(name=recv_data.get('name'))
        
        '''partial=True:只需要校验传入给序列化器的数据(data),其他的字段不校验.适用于部分数据更新'''
        ser = StudentSerializer(data=recv_data, partial=True)
        if ser.is_valid():
            ser.save()
            return JsonResponse(data={'msg': 'ok'})
        
        #  校验失败,返回错误信息
        return JsonResponse(data={'msg': ser.errors})

4.8 Field字段参数:read_only和write_only

read_only和write_only是field字段里面的参数,也是用来对字段做限制的

from rest_framework import serializers

class StudentSerizlizer(serializers.Serializer):
    '''read_only=True,序列化时序列化出该字段数据,反序列化校验时不要校验这个数据'''
    id = serializers.IntegerField(read_only=True)
    
    name = serializers.CharField(max_length=4,)

    '''write_only=True,序列化时不序列化这个字段数据,反序列校验时需要客户端传递这个数据过来进行校验'''
    age = serializers.IntegerField(max_value=18,write_only=True)
    
    class_null = serializers.CharField()

    description = serializers.CharField(allow_blank=True)

4.9 ModelSerializer的使用(推荐)

序列化与反序列化

1 定义一个序列化器,继承serializers.ModelSerializer:

from rest_framework import serializers
from students.models import Student

class StudentModelSerializer(serializers.ModelSerializer):
    # 如果模型类序列化器,必须 1.声明本次调用是哪个模型,2.模型里面的哪些字段
    class Meta:
        model = Student
        fields = ["id","name","age","description","sex"]
        # fields = "__all__" # 表示操作模型中的所有字段
        
        # 添加额外的验证选项
        exclude = ['id',] # 排除字段
        extra_kwargs = {
            "sex":{"write_only":True,},
            "id":{"read_only":True,}
        }

2 视图文件

from django.shortcuts import render
from django.http import JsonResponse

from django.views import View
from .serializers import StudentModelSerializer
from .models import Student

class StudentView(View):
    def get(self,request):
        all = Student.objects.all()
        model_ser = StudentModelSerializer(instance=all,many=True)
        return JsonResponse(model_ser.data, safe=False, json_dumps_params={'ensure_ascii':False})

    def post(self,request):
        print(request.POST)
        obj = StudentModelSerializer(data=request.POST)
        if obj.is_valid():
            print(obj.validated_data)
            obj.save()  # 上面使用的ModelSerializer,所以不需要我们自己写create方法了,自动保存
            return JsonResponse({'msg':'success'})
        print(obj.errors)
        print(obj.validated_data)
        return JsonResponse({'xx':'ssssss'})

5. 视图

5.1 request对象和response对象

request对象 在get方法中request.GET<=>request.query_params

在post方法中:

当发送的数据格式为…urlencoded类型时,request.POST<=>request.data

当发送过来的是json类型数据时,我们使用request.data属性能够获取到数据,得到的是普通字典类型。(如果是多选数据,不能使用getlist方法获取)

from rest_framework.views import APIView

class UserView(APIView):

    def get(self,request):
        print(request.GET)
        print(request.query_params)
        # request.GET和request.query_params是等价的
        return JsonResponse({'xx':'xxxxx'})

    def post(self,request):
        print(request)
        # 当发送的数据格式为..urlencoded类型时,request.POST和request.data等价
        # print(request.POST.getlist('hobby')) #<QueryDict: {'name': ['小林2'], 'age': ['6']}>
        # 当发送过来的是json类型数据时,我们使用request.data属性能够获取到数据
        print(request.data) #{'username': 'xxxx', 'password': '123'},普通字典类型
        # request.data能够获取到客户端发送过来的json类型数据,但是得到的结果为普通字典类型,但是如果是多选数据,不能使用getlist方法获取

        return JsonResponse({'xx': 'xxxxx'})

response对象

查看drf的默认配置
from rest_framework import settings

引入response
from rest_framework.response import Response



# 用户配置替换drf内部配置的写法
REST_FRAMEWORK = {
    'DEFAULT_RENDERER_CLASSES': (  # 默认响应渲染类
        'rest_framework.renderers.JSONRenderer',  # json渲染器
        'rest_framework.renderers.BrowsableAPIRenderer',  # 浏览器API渲染器
    )
}

通过浏览器访问:

注释浏览器API渲染器(得到的就是一个Json对象):

response的方法的相关参数

Response(data, status=None, template_name=None, headers=None, content_type=None)
  • data: 响应的序列化数据。

  • status: 响应的状态代码。默认为200。

  • template_name: 选择 HTMLRenderer 时使用的模板名称。

  • headers: 设置 HTTP header,字典类型。

  • content_type: 响应的内容类型,通常渲染器会根据内容协商的结果自动设置,但有些时候需要手动指定。

  • Response 可以将内置类型转换成字符串。写到data的位置

响应状态码

from rest_framework import status

# 响应状态码方式一
return Response({'xx':'xxxxx'}, status=201)

# 响应状态码方式二
return Response({'xx':'xxxxx'}, status=status.HTTP_201_CREATED)

所有状态码:

HTTP_100_CONTINUE = 100
HTTP_101_SWITCHING_PROTOCOLS = 101
HTTP_200_OK = 200
HTTP_201_CREATED = 201
HTTP_202_ACCEPTED = 202
HTTP_203_NON_AUTHORITATIVE_INFORMATION = 203
HTTP_204_NO_CONTENT = 204
HTTP_205_RESET_CONTENT = 205
HTTP_206_PARTIAL_CONTENT = 206
HTTP_207_MULTI_STATUS = 207
HTTP_208_ALREADY_REPORTED = 208
HTTP_226_IM_USED = 226
HTTP_300_MULTIPLE_CHOICES = 300
HTTP_301_MOVED_PERMANENTLY = 301
HTTP_302_FOUND = 302
HTTP_303_SEE_OTHER = 303
HTTP_304_NOT_MODIFIED = 304
HTTP_305_USE_PROXY = 305
HTTP_306_RESERVED = 306
HTTP_307_TEMPORARY_REDIRECT = 307
HTTP_308_PERMANENT_REDIRECT = 308
HTTP_400_BAD_REQUEST = 400
HTTP_401_UNAUTHORIZED = 401
HTTP_402_PAYMENT_REQUIRED = 402
HTTP_403_FORBIDDEN = 403
HTTP_404_NOT_FOUND = 404
HTTP_405_METHOD_NOT_ALLOWED = 405
HTTP_406_NOT_ACCEPTABLE = 406
HTTP_407_PROXY_AUTHENTICATION_REQUIRED = 407
HTTP_408_REQUEST_TIMEOUT = 408
HTTP_409_CONFLICT = 409
HTTP_410_GONE = 410
HTTP_411_LENGTH_REQUIRED = 411
HTTP_412_PRECONDITION_FAILED = 412
HTTP_413_REQUEST_ENTITY_TOO_LARGE = 413
HTTP_414_REQUEST_URI_TOO_LONG = 414
HTTP_415_UNSUPPORTED_MEDIA_TYPE = 415
HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE = 416
HTTP_417_EXPECTATION_FAILED = 417
HTTP_418_IM_A_TEAPOT = 418
HTTP_422_UNPROCESSABLE_ENTITY = 422
HTTP_423_LOCKED = 423
HTTP_424_FAILED_DEPENDENCY = 424
HTTP_426_UPGRADE_REQUIRED = 426
HTTP_428_PRECONDITION_REQUIRED = 428
HTTP_429_TOO_MANY_REQUESTS = 429
HTTP_431_REQUEST_HEADER_FIELDS_TOO_LARGE = 431
HTTP_451_UNAVAILABLE_FOR_LEGAL_REASONS = 451
HTTP_500_INTERNAL_SERVER_ERROR = 500
HTTP_501_NOT_IMPLEMENTED = 501
HTTP_502_BAD_GATEWAY = 502
HTTP_503_SERVICE_UNAVAILABLE = 503
HTTP_504_GATEWAY_TIMEOUT = 504
HTTP_505_HTTP_VERSION_NOT_SUPPORTED = 505
HTTP_506_VARIANT_ALSO_NEGOTIATES = 506
HTTP_507_INSUFFICIENT_STORAGE = 507
HTTP_508_LOOP_DETECTED = 508
HTTP_509_BANDWIDTH_LIMIT_EXCEEDED = 509
HTTP_510_NOT_EXTENDED = 510
HTTP_511_NETWORK_AUTHENTICATION_REQUIRED = 511

5.2 基于 APIView 视图

APIView总结

1.APIView中封装了自己的request和response

2.django:request.GET对应drf中的request.query_params

3.django:request.POST对应drf中的request.data

4.APIView中的response不需要写参数safe=False,ensure_ascii这些东西了,因为这些功能已经封装在response里面了

5.当我们在浏览器上访问一个url,给你回复的是一个界面。如果我们用postman访问url,给你返回的就是json数据。这是因为response中有相关配置导致的。

APIView实现代码

# 序列器
from rest_framework import serializers
from app01 import models

class StudentModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Student
        fields = '__all__'
# 路由
urlpatterns = [
    path('students/', views.StudentAPIView.as_view()),
    re_path(r'students/(?P<pk>\d+)', views.StudentAPIView.as_view()),
]
from .serializers import  StudentSerializer
from .models import Student
from rest_framework.viewsets import ModelViewSet
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status

class StudentAPIView(APIView):
    # 获取所有数据接口
    # def get(self, request):
    #     all = Student.objects.all()  # 获取数据库中Student表中所有数据
    #     ser = StudentSerializer(instance=all, many=True)  # 将后端数据序列化
    #     return Response(ser.data)  # 将序列化之后的数据传递给前端

    # 获取所有数据接口、获取单条记录
    def get(self, request, pk=None):
        try:
            pk = request.GET.get('pk', default=None)
            if pk:
                one = Student.objects.get(pk=pk)
                ser = StudentSerializer(instance=one)
                return Response(ser.data)
        except:
            return Response(status=status.HTTP_404_NOT_FOUND)

        all = Student.objects.all()  # 获取数据库中Student表中所有数据
        ser = StudentSerializer(instance=all, many=True)  # 将后端数据序列化
        return Response(ser.data)  # 将序列化之后的数据传递给前端

    # 添加单条记录的接口
    def post(self, reqeust):
        data = reqeust.data  # 获取用户在前端输入的数据
        ser = StudentSerializer(data=data)  # 将数据反序列化
        if ser.is_valid():    # 校验
            instance = ser.save()  # 添加新记录对象到数据库
            # 据API规范, 数据保存之后应该显示到前端上, 所以需要将新添加的数据序列化
            ser = StudentSerializer(instance=instance)
            return Response(ser.data, status=status.HTTP_201_CREATED)
        return Response(ser.errors)

    # 更新单条记录接口, 接受 JSON 数据
    def put(self, request):
        data = request.data
        if data:
            try:
                obj = Student.objects.get(pk=data.get('id'))
                if obj:
                    ser = StudentSerializer(
                        instance=obj, data=data, partial=True)
                    if ser.is_valid():
                        instance = ser.save()  # instance为添加的新纪录对象
                        ser = StudentSerializer(instance=instance)
                        return Response(ser.data)
                    return Response(ser.errors, status=status.HTTP_400_BAD_REQUEST)
            except:
                return Response(status=status.HTTP_404_NOT_FOUND)
        return Response(status=status.HTTP_400_BAD_REQUEST)

    # 删除单条记录
    def delete(self, request):
        try:
            data = request.data
            Student.objects.get(pk=data.get('id')).delete()
            return Response(status=status.HTTP_204_NO_CONTENT)
        except:
            return Response(status=status.HTTP_400_BAD_REQUEST)

5.3 基于 GenericAPIView 视图

GenerivAPIView继承自APIView方法,主要增加了操作序列化器和数据库查询的方法。

GenericAPIView中的属性和方法

  • serializer_class 指明视图使用的序列化器

  • get_serializer(self, *args, **kwargs) 返回序列化器对象,主要用来提供给Mixin扩展类使用,如果我们在视图中想要获取序列化器对象,也可以直接调用此方法

  • queryset 指明使用的数据查询集

  • get_queryset(self) 返回视图使用的查询集,主要用来提供给Mixin扩展类使用,是列表视图与详情视图获取数据的基础,默认返回queryset属性,可以重写

  • get_object(self) 返回详情视图所需的模型类数据对象,主要用来提供给Mixin扩展类使用。在视图中可以调用该方法获取详情信息的模型类对象。

  • *get_serializer_class(self) 当出现一个视图类中调用多个序列化器时,那么可以通过条件判断在get_serializer_class方法中通过返回不同的序列化器类名就可以让视图方法执行不同的序列化器对象了。

            返回序列化器类,默认返回serializer_class

# 路由:
from django.urls import path, re_path
from . import views
urlpatterns = [
    path('student2/', views.StudentViews21.as_view()),
    re_path(r'student2/(?P<pk>\d+)/', views.StudentViews.as_view()),
]
from rest_framework.generics import GenericAPIView


class StudentViews(GenericAPIView):
    queryset = models.Student.objects.all()  # 指明要使用的数据查询集[必须指定]
    serializer_class = StudentSerializer  # 指明视图要使用的序列化器[可写可不写]

    # 通过get_serializer_class来控制不同条件下,使用不同的序列化器类
    def get_serializer_class(self):
        if self.request.method == 'GET':
            return Student2Serializer
        else:
            return StudentSerializer

    # 获取所有数据接口
    def get(self, request):
        # all_data = models.Student.objects.all()
        # serializer = StudentSerializer(instance=all_data, many=True)
        serializer = self.get_serializer(instance=self.get_queryset(), many=True)
        return Response(serializer.data)

    # 添加一条记录的接口
    def post(self, request):
        data = request.data
        serializer = self.get_serializer(data=data)
        if serializer.is_valid():
            instance = serializer.save()  # instance为添加的新纪录对象
            serializer = self.get_serializer(instance=instance)

            return Response(serializer.data, status=status.HTTP_201_CREATED)

        else:
            print(serializer.errors)
            return Response({'error':'字段错误'})

class StudentViews(GenericAPIView):
    queryset = models.Student.objects.all()
    serializer_class = StudentSerializer

    # 获取单条记录
    def get(self, request, pk):
        # stu_obj = models.Student.objects.get(pk=pk)
        serializer = self.get_serializer(instance=self.get_object())
        return Response(serializer.data)

    # 更新单条记录
    def put(self, request, pk):
        # stu_obj = models.Student.objects.get(pk=pk)
        data = request.data
        serializer = self.get_serializer(instance=self.get_object(), data=data, partial=True)
        if serializer.is_valid():
            # print('>>>',serializer.data)  #在save方法之前,不能调用data属性,serializer.data
            instance = serializer.save()  # instance为添加的新纪录对象
            print(serializer.data)  #之后可以看
            serializer = self.get_serializer(instance=instance)

            return Response(serializer.data)
        else:
            print(serializer.errors)

    # 删除单条记录

    def delete(self, request, pk):

        models.Student.objects.get(pk=pk).delete()
        return Response(None, status=status.HTTP_204_NO_CONTENT)

5.4 基于Mixin个视图扩展类视图

提供了几种后端视图(对数据资源进行增删改查)处理流程的实现,如果需要编写的视图属于这五种,则视图可以通过继承相应的扩展类来复用代码,减少自己编写的代码量。

这五个扩展类需要搭配GenericAPIView父类,因为五个扩展类的实现需要调用GenericAPIView提供的序列化器与数据库查询的方法。

换句话说。就是你在视图中的 get post put 等这些方法中不用写里面的代码了,里面的代码相关的操作已经被封装到对应的Mixin中了。

相比于GenericAPIView,Mixin拓展封装了:调用序列器序列化与反序列化的过程

  • ListModelMixin: self.list(request)

  • CreateModelMixin: self.create(request)

  • RetrieveModelMixin:self.retrieve(request,pk)

  • UpdateModelMixin:self.update(request,pk)

  • DestroyModelMixin:self.destroy(request,pk)

# views.py
class StudentViews1(GenericAPIView, ListModelMixin, CreateModelMixin):
    queryset = Student.objects.all()
    serializer_class = StudentSerializer

    # 获取所有数据接口
    def get(self, request):
        return self.list(request)

    # 添加一条记录的接口
    def post(self, request):
        return self.create(request)


class StudentView2(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
    queryset = Student.objects.all()
    serializer_class = StudentSerializer

    # 获取单条记录
    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)

5.5 基于GenericAPIView的视图子类 APIView

上面的代码还是过于麻烦,因为既要继承GenericAPIView又要继承Mixin系列的类。

所以将各自的操作封装成自己的APIView类。用哪个继承哪个。

而且连函数都不用写了,在???APIView类中已经有了,所以不用再写了,继承自己的APIView类即可。

class StudentViews1(ListAPIView, CreateAPIView):
    queryset = Student.objects.all()
    serializer_class = StudentSerializer


class StudentViews2(RetrieveAPIView, UpdateAPIView, DestroyAPIView):
    queryset = Student.objects.all()
    serializer_class = StudentSerializer

5.6 ViewSet

主要解决问题:之前我们在写视图类的时候,获取多条数据和提交数据放在了一个类中。获取单条数据,更新单条数据,删除多条数据放到了一个类中

这是因为多条数据操作时不需要指定pk值,而针对单条数据时,需要指定pk值。也就是说需要知道你要操作哪一条数据

这样我们就很矛盾。我们没法将5个方法放到同一个类中。

而下面的ViewSet就可以解决这个问题,将5个方法放到同一个类中

from rest_framework.viewsets import ViewSet

class Students5View(ViewSet):
    # 获取所有数据接口
    def get_all_student(self,request):  # action
        all_data = models.Student.objects.all()
        serializer = StudentSerializer(instance=all_data,many=True)
        return Response(serializer.data)

    # 添加一条记录的接口
    def add_student(self,request):
        data = request.data
        serializer = StudentSerializer(data=data)
        if serializer.is_valid():
            instance = serializer.save()  #instance为添加的新纪录对象
            serializer = StudentSerializer(instance=instance)

            return Response(serializer.data,status=status.HTTP_201_CREATED)

        else:
            print(serializer.errors)

    def get_one(self,request,pk):
        stu_obj = models.Student.objects.get(pk=pk)
        serializer = StudentSerializer(instance=stu_obj)
        return Response(serializer.data)

通过上面代码我们可以看出。**单条数据操作和多条数据被放到了一个类中。**并且方法名也不再局限于必须要使用get post put...这些方法名了。

这是因为在urls.py中的as_view方法做了一个请求方法和对应函数名的指定。

而获取单条数据和获取多条数据能够共存在一个类中,是因为他们处于两个url中。虽然都是get请求过去的,但是他们在不同的url中,会执行各自的视图方法

通过下面url,我们就可以发挥出ViewSet的用处了:

path('students5/', views.Students5View.as_view({'get':'get_all_student','post':'add_student'})),
re_path('students5/(?P<pk>\d+)/', views.Students5View.as_view({'get':'get_one'})),

5.7 ViewSet + APIView

"""发挥下ViewSet的作用"""
from rest_framework.viewsets import ViewSet
from rest_framework.generics import ListAPIView,CreateAPIView,RetrieveAPIView

class Student2ViewSet(ViewSet,ListAPIView,CreateAPIView,RetrieveAPIView):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer
    
    def get_all(self,request):
        return self.list(request)

    def add_student(self,request):
        return self.create(request)

    def get_one(self,request,pk):
        return self.retrieve(request)

5.8 GenericViewSet + Mixin

效果和 Viewset + APIView 是相同的

from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import ListModelMixin,CreateModelMixin,RetrieveModelMixin

class Student3ViewSet(GenericViewSet,ListModelMixin,CreateModelMixin,RetrieveModelMixin):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer
    def get_all(self,request):
        return self.list(request)

    def add_student(self,request):

        return self.create(request)

    def get_one(self,request,pk):
        return self.retrieve(request)

5.9 以上方法改良后

urlpatterns = [
    path("students7/", views.Student4ViewSet.as_view({"get": "list", "post": "create"})),
    re_path("students7/(?P<pk>\d+)/", views.Student4ViewSet.as_view({"get": "retrieve","put":"update","delete":"destroy"})),
]
from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import ListModelMixin,CreateModelMixin,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin

class Student4ViewSet(GenericViewSet,ListModelMixin,CreateModelMixin,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer

5.10 基于ModelViewSet视图(终极版)

上面的代码,在导入和继承的时候太麻烦了,需要写5个Mixin类

这时出现了ModelViewSet,ModelViewSet同时继承ListModelMixin,CreateModelMixin,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin这五个类,这样写起来就更加简单了。

# serializer.py
class StudentSerializer(serializers.ModelSerializer):
    class Meta:
        model = Student
        fields = "__all__"
# urls.py
from django.urls import path, re_path
from . import views

urlpatterns = [
    path('student/', views.StudentModelViewSet.as_view({'get': 'list', 'post': 'create'})),
    re_path(r'student/(?P<pk>\d+)/', views.StudentModelViewSet.as_view({'get':'retrieve','put':'update','delete':'destroy'})),

]
# views.py
from rest_framework.viewsets import ModelViewSet

class StudentModelViewSet(ModelViewSet):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer

5.11 ReadOnlyViewset(不常用)

继承自GenericViewSet,同时包括了ListModelMixin、RetrieveModelMixin。

5.12 添加自定义动作(action装饰器)

在视图集中,除了上述默认的方法动作外,还可以添加自定义动作,进行扩展。

举例,比如做一个登录方法login:

from rest_framework.viewsets import ModelViewSet
from rest_framework.decorators import action

class StudentModelViewSet(ModelViewSet):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer

    # methods 设置当前方法允许哪些http请求访问当前视图方法
    # detail 设置当前视图方法是否是操作一个数据
    # detail为True,表示路径名格式应该为 router_stu/{pk}/login/
    @action(methods=['get'], detail=True)
    def login(self,request):  # 这个就可以称为自定义的action动作
        """学生登录功能"""
        return Response({"message":"登录成功"})
    
    # detail为False 表示路径名格式应该为 router_stu/get_new_5/
    @action(methods=['put'], detail=False)
    def get_new_5(self,request):
        """获取最近添加的5个学生信息"""
        print(self.action) # 获取本次请求的视图方法名
        
        
'''通过路由访问到当前方法中.可以看到本次的action就是请求的方法名''
# urls.py
urlpatterns = [
    path("students/", views.StudentModelViewSet.as_view({"get": "list", "post": "create"})),
    re_path("students/(?P<pk>\d+)/",
            views.StudentModelViewSet.as_view({"get": "retrieve", "put": "update", "delete": "destroy"})),

    path("students/login/",views.StudentModelViewSet.as_view({"get":"login"}))

]

6. 路由:Routers

6.1 如何添加路由数据

如何添加路由数据,并且和视图函数做关联:

from django.urls import path, re_path
from . import views
urlpatterns = [
    ...
]

"""使用drf提供路由类router给视图集生成路由列表"""
# 实例化路由类
# drf提供一共提供了两个路由类给我们使用,他们用法一致,功能几乎一样
from rest_framework.routers import DefaultRouter
router = DefaultRouter()

# 注册视图集
# router.register("路由前缀",视图集类)
router.register("router_stu",views.StudentModelViewSet)

# 把生成的路由列表追加到urlpatterns
urlpatterns += router.urls

上面的代码就成功生成了路由地址[增/删/改/查一条/查多条的功能],但是不会自动帮我们在视图集自定义方法的路由。

所以我们如果也要给自定义方法生成路由,则需要进行action动作的声明。

6.2 在视图集中附加action声明

在视图集中,如果想要让Router自动帮助我们为自定义的动作生成路由信息,需要使用rest_framework.decorators.action装饰器。

以action装饰器装饰的方法名会作为action动作名,与list、retrieve等同。

action装饰器可以接收两个参数:

  • methods: 声明该action对应的请求方式,列表传递

  • detail:

    声明该action的路径是否与单一资源对应,及是否是 xxx//action方法名/

    • True 表示路径格式是xxx/<pk>/action方法名/

    • False 表示路径格式是xxx/action方法名/

from rest_framework.viewsets import ModelViewSet
from rest_framework.decorators import action

class StudentModelViewSet(ModelViewSet):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer

    # methods 设置当前方法允许哪些http请求访问当前视图方法
    '''detail 设置当前视图方法是否是操作一个数据'''
    # detail为True,表示路径名格式应该为 router_stu/{pk}/login/
    @action(methods=['get'], detail=True)
    def login(self, request,pk):
        """登录"""
        ...

    # detail为False 表示路径名格式应该为 router_stu/get_new_5/
    @action(methods=['put'], detail=False)
    def get_new_5(self, request):
        """获取最新添加的5个学生信息"""
        ...

6.3 DefaultRouter和SimpleRouter的区别(了解)

DefaultRouter与SimpleRouter的区别是,DefaultRouter会多附带一个默认的API根视图,返回一个包含所有列表视图的超链接响应数据。

比如使用DefaultRouter时你访问一下http://127.0.0.1:8001/router_stu/ 会看到一个页面。

而SimpleRouter会报错,一般都需要有个查看所有接口的页面,所以我们基本都是用的是DefaultRouter

附:APIView思维导图

6.5 附:drf的执行流程图

参考: https://www.cnblogs.com/libolun/p/13861557.html

7. 认证/权限/限流/过滤/排序/分页/异常处理

7.1 认证 Authentication

可以在配置文件中配置全局默认的认证方案

from rest_framework import settings

'''在settings配置文件中,我们可以进行下面的配置来覆盖默认配置'''
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
      # 哪个写在前面,优先使用哪个认证
        
        'rest_framework.authentication.SessionAuthentication',  # session认证,admin后台其实就使用的session认证,其实接口开发很少用到session认证,所以我们通过配置可以改为其他认证,比如后面项目里面我们用到jwt,JSON WEB TOKEN认证,或者一些配合redis的认证
        
        'rest_framework.authentication.BasicAuthentication',   # 基本认证,工作当中可能一些测试人员会参与的话,他们会将一些认证数据保存在内存当中,然后验证的,我们基本上用不上
    )
}

也可以在每个视图中通过设置authentication_classes属性来设置,比如说我们很多接口的数据都是可以让别人获取数据的,

但是有可能有些接口是调用给别人网站的,有可能到时候我们就需要一些单独的认证了

# views.py
from rest_framework.authentication import SessionAuthentication, BasicAuthentication
from rest_framework.views import APIView

class ExampleView(APIView):
    # 类属性
    authentication_classes = [SessionAuthentication, BasicAuthentication] # 也可以写成元组形式的,到时候我们使用我们自己开发的认证组件的时候,就需要自己写一个认证组件类,然后写在列表中来使用
    ...

认证失败会有两种可能的返回值:

  • 401 Unauthorized 未认证

  • 403 Permission Denied 权限被禁止

7.2 自定义认证

from rest_framework.authentication import BaseAuthentication      # 引入基础的认证rest_framework基础的认证类
from rest_framework.exceptions import AuthenticationFailed        # 引入认证失败的错误

class APIAuth(BaseAuthentication):
    def authenticate(self, request):
        print(request) 

        if 1:
            return 'xx','oo'  # request.user = 'xx'  request.auth = 'oo'

        else:
            raise AuthenticationFailed('认证失败')

全局使用,settings配置文件中使用

REST_FRAMEWORK = {

    'DEFAULT_AUTHENTICATION_CLASSES': (
        #哪个写在前面,优先使用哪个认证
        'use_view.utils.auth.Auth', # 路径,配置的是全局生效。在setting文件中注册该自定义的类
        'rest_framework.authentication.SessionAuthentication', 
        'rest_framework.authentication.BasicAuthentication', 
    ),
}

局部视图中使用

from rest_framework.authentication import SessionAuthentication, BasicAuthentication
from four.utils.auth import APIAuth

class AuthAPIView(APIView):
    authentication_classes = [APIAuth,]         # 自定义的认证,在本视图中生效
    def get(self,request):
        print('>>>>',request.user)  # AnonymousUser  匿名用户,假用户
        print('>>>>',request.auth)  # AnonymousUser  匿名用户,假用户
        #>>>> root
        return Response({'msg':'hello'})

7.3 权限:Permissions

权限控制可以限制用户对于视图的访问和对于具体数据对象的访问。

  • 在执行视图的dispatch()方法前,会先进行视图访问权限的判断

  • 在通过get_object()获取具体对象时,会进行模型对象访问权限的判断

全局设置默认的权限管理类

REST_FRAMEWORK = {
    ....
    
    'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.IsAuthenticated', # 登录状态下才能访问我们的接口,可以通过退出admin后台之后,你看一下还能不能访问我们正常的接口就看到效果了
    )
}

局部设置实例

from rest_framework.authentication import SessionAuthentication
from rest_framework.permissions import IsAuthenticated
from rest_framework.generics import RetrieveAPIView

class StudentAPIView(RetrieveAPIView):
    queryset = Student.objects.all()
    serializer_class = StudentSerializer
    authentication_classes = [SessionAuthentication]
    permission_classes = [IsAuthenticated]             # 设置当前视图只有认证的用户才能访问
  • AllowAny 允许所有用户,未指明时默认为允许所有用户

  • IsAuthenticated 仅通过认证的用户

  • IsAdminUser 仅管理员用户(可以通过admin创建一个用户进行测试)

  • IsAuthenticatedOrReadOnly 已经登陆认证的用户可以对数据进行增删改操作,没有登陆认证的只能查看数据。

7.4 自定义权限

如需自定义权限,需继承rest_framework.permissions.BasePermission父类,并实现以下两个任何一个方法或全部

  • .has_permission(self, request, view)

    是否可以访问视图, view表示当前视图对象

  • .has_object_permission(self, request, view, obj)

    是否可以访问数据对象, view表示当前视图, obj为数据对象

例如在当前子应用下,创建一个权限文件permissions.py中声明自定义权限类:

from rest_framework.permissions import BasePermission

class MyPermission(BasePermission):
    def has_permission(self, request, view):
        if request.user.username == 'xx':
            return True
        return False

视图函数

from .permissions import MyPermission
from rest_framework.viewsets import ModelViewSet
from .serializer import StudentModelSerializer

class StudentViewSet(ModelViewSet):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer
    permission_classes = [MyPermission]

7.5 限流:Throttling

可以对接口访问的频次进行限制,以减轻服务器压力。

一般用于付费购买次数,投票等场景使用.

全局配置

REST_FRAMEWORK = {
  
    'DEFAULT_THROTTLE_CLASSES': (
        'rest_framework.throttling.AnonRateThrottle', # 匿名用户,未登录的
        'rest_framework.throttling.UserRateThrottle', # 经过登录之后的用户
    ),
    'DEFAULT_THROTTLE_RATES': {
        'anon': '100/m',
        'user': '1000/m',
    }
}

# {'s': 1, 'm': 60, 'h': 3600, 'd': 86400} m表示分钟,可以写m,也可以写minute

DEFAULT_THROTTLE_RATES 可以使用 second, minute, hour 或day来指明周期。

仅局部配置

from rest_framework.throttling import UserRateThrottle
from rest_framework.views import APIView

class ExampleView(APIView):
    throttle_classes = (UserRateThrottle,)  # 局部配置
    ...

7.6 可选择的限流类

1) AnonRateThrottle

限制所有匿名未认证用户,使用IP区分用户。

使用DEFAULT_THROTTLE_RATES['anon'] 来设置频次

2)UserRateThrottle

限制认证用户,使用User id 来区分。

使用DEFAULT_THROTTLE_RATES['user'] 来设置频次

3)ScopedRateThrottle (待定...)

限制用户对于每个视图的访问频次,使用ip或user id,先找的用户id,没有设置用户id的话就会使用ip地址。

例如: 全局

全局配置中设置访问频率

REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_CLASSES': (
        'rest_framework.throttling.AnonRateThrottle', # 匿名用户,未登录的
        'rest_framework.throttling.UserRateThrottle', # 经过登录之后的用户
    ),
    'DEFAULT_THROTTLE_RATES': {
        'anon': '3/minute',
        'user': '10/minute'
    }
}
from rest_framework.authentication import SessionAuthentication
from rest_framework.permissions import IsAuthenticated
from rest_framework.generics import RetrieveAPIView
from rest_framework.throttling import UserRateThrottle

class StudentAPIView(RetrieveAPIView):
    queryset = Student.objects.all()
    serializer_class = StudentSerializer
    authentication_classes = [SessionAuthentication]
    permission_classes = [IsAuthenticated]
    throttle_classes = (UserRateThrottle,)

ScopedRateThrottle 局部使用示例

# settings.py内容
REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_CLASSES': (
        'rest_framework.throttling.AnonRateThrottle', # 匿名用户,未登录的
        'rest_framework.throttling.UserRateThrottle', # 经过登录之后的用户
    ),
    'DEFAULT_THROTTLE_RATES': {
        'xx': '3/minute',
        'oo': '10/minute'
    }
}
    
# views.py内容

from rest_framework.throttling import ScopedRateThrottle

class StudentAPIView(ListAPIView):
    queryset = models.Student.objects.all()
    serializer_class = StudentModelSerializer
    throttle_classes = [ScopedRateThrottle,]
    throttle_scope = 'xx'

class StudentAPI2View(ListAPIView):
    queryset = models.Student.objects.all()
    serializer_class = StudentModelSerializer
    throttle_classes = [ScopedRateThrottle, ]
    throttle_scope = 'oo'
    
# urls.py内容
    path(r'students/',views.StudentAPIView.as_view()),
    path(r'students2/',views.StudentAPI2View.as_view()),

7.7 过滤:Filtering

对于列表数据可能需要根据字段进行过滤,我们可以通过添加django-fitlter扩展来增强支持。

pip install django-filter  

在配置文件中增加过滤后端的设置:

INSTALLED_APPS = [
    ...
    'django_filters',  # 需要注册应用,
]

REST_FRAMEWORK = {
    ...
    'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',)
}

在视图中添加 filter_fields 属性,指定可以过滤的字段

class StudentListView(ListAPIView):
    queryset = Student.objects.all()
    serializer_class = StudentSerializer
    filter_fields = ('age', 'sex')

# 127.0.0.1:8000/four/students/?sex=1

7.8 排序

  1. 添加 filter_backends 属性(使用OrderingFilter过滤器)

  2. 指定字段,通过 **ordering_fields **属性指定需要按某字段排序

from rest_framework.filters import OrderingFilter

class StudentListView(ListAPIView):
    queryset = Student.objects.all()
    serializer_class = StudentSerializer
    filter_backends = [OrderingFilter]
    filter_fields = ('age', 'sex')
    
# 127.0.0.1:8000/books/?ordering=-age 
# 必须是ordering=某个值
# -id 表示针对id字段进行倒序排序
# id  表示针对id字段进行升序排序

7.9 排序 + 过滤

如果需要在过滤以后再次进行排序,则需要两者结合!

需要使用 DjangoFilterBackend

from rest_framework.generics import ListAPIView
from students.models import Student
from .serializers import StudentModelSerializer
from django_filters.rest_framework import DjangoFilterBackend # 需要使用一下它才能结合使用
class Student3ListView(ListAPIView):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer
    filter_fields = ('age', 'sex')
    
    # 因为filter_backends是局部过滤配置,局部配置会覆盖全局配置,所以需要重新把过滤组件核心类再次声明,
    # 否则过滤功能会失效
    filter_backends = [OrderingFilter,DjangoFilterBackend]
    
    ordering_fields = ('id', 'age')
    
    
    # 针对的是继承的类中的list方法
    
# 127.0.0.1:8000/books/?sex=1&ordering=-age 

7.10 分页:Pagination

REST framework提供了分页的支持。

我们可以在配置文件中设置全局的分页方式

REST_FRAMEWORK = {
  # 全局分页,一旦设置了全局分页,那么我们drf中的视图扩展类里面的list方法提供的列表页都会产生分页的效果。所以一般不用全局分页
    'DEFAULT_PAGINATION_CLASS':  'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 100  # 每页最大数据量
}

也可通过自定义Pagination类,来为视图添加不同分页行为。在视图中通过pagination_class属性来指明。

class LargeResultsSetPagination(PageNumberPagination):
    page_size = 1000  # 默认每页显示多少条
    #127.0.0.1:8001/students/?page=5&page_size=10
    
    page_size_query_param = 'page_size'  # 自定义页码的参数
    max_page_size = 10000         # 客户端请求数量最多不能超过的数值
    
class BookDetailView(RetrieveAPIView):
    queryset = BookInfo.objects.all()
    serializer_class = BookInfoSerializer
    pagination_class = LargeResultsSetPagination

注意:如果在视图内关闭分页功能,只需在视图内设置

pagination_class = None

7.10.1 PageNumberPagination

前端访问网址形式

GET  http://127.0.0.1:8000/students/?page=4

可以在子类中定义的属性:

  • page_size 每页数目

  • page_query_param 前端发送的页数关键字名,默认为"page"

  • page_size_query_param 前端发送的每页数目关键字名,默认为None

  • max_page_size 前端最多能设置的每页数量

# 声明分页的配置类
from rest_framework.pagination import PageNumberPagination
class StandardPageNumberPagination(PageNumberPagination):
    # 默认每一页显示的数据量
    page_size = 2
    # 允许客户端通过get参数来控制每一页的数据量
    page_size_query_param = "size"
    max_page_size = 10  # 客户端通过size指定获取数据的条数时,最大不能超过多少
    # 自定义页码的参数名
    page_query_param = "p"

class StudentAPIView(ListAPIView):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer
    pagination_class = StandardPageNumberPagination

# 127.0.0.1/four/students/?p=1&size=5

7.10.2 LimitOffsetPagination

前端访问网址形式:其实就是通过偏移量来取数据

GET http://127.0.0.1/four/students/?limit=100&offset=400  # 从下标为400的记录开始,取100条记录

可以在子类中定义的属性:

  • default_limit 默认限制,每页数据量大小,默认值与PAGE_SIZE设置一致

  • limit_query_param limit参数名,默认'limit' , 可以通过这个参数来改名字

  • offset_query_param offset参数名,默认'offset' ,可以通过这个参数来改名字

  • max_limit 最大limit限制,默认None, 无限制

from rest_framework.pagination import LimitOffsetPagination

class StandardLimitOffsetPagination(LimitOffsetPagination):
    # 默认每一页查询的数据量,类似上面的page_size
    default_limit = 2
    limit_query_param = "size"  # 默认是limit
    offset_query_param = "start"  # 默认是offset

class StudentAPIView(ListAPIView):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer
    # 调用页码分页类
    # pagination_class = StandardPageNumberPagination
    # 调用查询偏移分页类
    pagination_class = StandardLimitOffsetPagination

7.11 异常处理:Exception

7.11.1 自定义异常的捕获

先看一个简单的实例:

视图:

class ApiError(Exception):
    pass

class Test(APIView):
    def get(self,request,pk):
        try:
            one = models.Student.objects.get(pk=pk)
        except models.Student.DoesNotExist:
            raise ApiError('查询出错!!!')
        ser = StudentModelSerializer(instance=one)
        return Response(ser.data)

当索引超出范围后,会抛出错误:

在REST framework中提供了异常处理,我们可以自定义异常处理函数,来捕获异常反馈前端

1 在应用下新建一个文件exceptions.py,

from rest_framework.views import exception_handler
from .views import ApiError
from rest_framework.response import Response

def custom_exception_handler(exc, context): # 自定义的错误处理函数
    # 先调用REST framework默认的异常处理方法获得标准错误响应对象
    response = exception_handler(exc, context) #这个函数是drf提供的,它处理了一些错误,但是如果它处理不了的,它会返回None,所以,如果是None的话,我们需要自己来处理错误
    
    # 在此处补充自定义的异常处理
    if response is None:
        if isinstance(exc,ApiError):
            #这里就可以记录错误信息了,一般记录到文件中,可以使用日志系统来进行记录
            return Response({'msg':'自定义API错误了'})
            #response.data['status_code'] = response.status_code
    return response

2 在setting文件中配置REST_FRAMEWORK

REST_FRAMEWORK = {
    'EXCEPTION_HANDLER': 'my_project.my_app.utils.custom_exception_handler'

这样就完成了自定义异常的捕获:

同理,补充上处理关于数据库的异常:

from rest_framework.views import exception_handler as drf_exception_handler
from rest_framework import status
from django.db import DatabaseError

def exception_handler(exc, context):
    response = drf_exception_handler(exc, context)

    if response is None:
        view = context['view'] # 出错的方法或者函数名称
        if isinstance(exc, DatabaseError):
            print('[%s]: %s' % (view, exc))
            response = Response({'detail': '服务器内部错误'}, status=status.HTTP_507_INSUFFICIENT_STORAGE)

    return response

7.11.2 REST framework默认异常

  • APIException 所有异常的父类

  • ParseError 解析错误

  • AuthenticationFailed 认证失败

  • NotAuthenticated 尚未认证

  • PermissionDenied 权限决绝

  • NotFound 未找到

  • MethodNotAllowed 请求方式不支持

  • NotAcceptable 要获取的数据格式不支持

  • Throttled 超过限流次数

  • ValidationError 校验失败

也就是说,上面列出来的异常不需要我们自行处理了,很多的没有在上面列出来的异常,就需要我们在自定义异常中自己处理了。

8. 自动生成接口文档

8.1 安装依赖

pip3 install coreapi

8.2 配置路由

from django.urls import path, include #path:直接写路径,re_path:可以写正则匹配
from rest_framework.documentation import include_docs_urls

urlpatterns = [
    ...
    path('docs/', include_docs_urls(title='站点页面标题'))
]

8.3 配置setting文件

REST_FRAMEWORK = {
    ...
    'DEFAULT_SCHEMA_CLASS': "rest_framework.schemas.AutoSchema",

}

这样一个接口文档就自动生成了。

8.4 文档描述说明的定义位置

1) 单一方法的视图,可直接使用类视图的文档字符串,如

class BookListView(generics.ListAPIView):
    """
    get: 返回所有图书信息.
    post: 添加记录
    """
    #注意,这是在类中声明的注释,如果在方法中你声明了其他注释,会覆盖这个注释的

2)包含多个方法的视图,在类视图的文档字符串中,分开方法定义,如

class BookListCreateView(generics.ListCreateAPIView):
    """
    get:
    返回所有图书信息.

    post:
    新建图书.
    """

3)对于视图集ViewSet,仍在类视图的文档字符串中分开定义,但是应使用action名称区分,如

class BookInfoViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, GenericViewSet):
    """
    list:
    返回图书列表数据

    retrieve:
    返回图书详情数据

    latest:
    返回最新的图书数据

    read:
    修改图书的阅读量
    """

4) 视图集ViewSet中的 retrieve 名称,在接口文档网站中叫做read

5)参数的Description需要在模型类或序列化器类的字段中以help_text选项定义

或 注意,如果你多个应用使用同一个序列化器,可能会导致help_text的内容显示有些问题,小事情

class StudentSerializer(serializers.ModelSerializer):
    class Meta:
        model = Student
        fields = "__all__"
        extra_kwargs = {
            'age': {
                'required': True,
                'help_text': '年龄'
            }
        }
PreviousDRF接口 + Vue实现下载文件NextAPI跨域设置

Last updated 3 years ago