😁
运维笔记
运维笔记
运维笔记
  • 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
  • 搭建项目
  • 路由器
  • 请求数据
  • 请求参数
  • 请求体
  • Form表单
  • 文件上传
  • 响应体Schema
  • Model的Schema
  • 认证
  • 操作参数
  • 版本控制
  • 请求解析器
  • 响应渲染器
  • 错误处理
  • 覆盖默认异常处理程序
  • CSRF
  • 异步支持
  1. PYTHON
  2. Django

Django Ninja

Django Ninja 教程

搭建项目

  • 安装

    pip3 install django-ninja
  • 创建django项目

    django-admin startproject myproject
  • 单应用项目

    • 创建api.py,与 url.py 同级

    from ninja import NinjaAPI
    
    api = NinjaAPI()
    
    @api.get('/hello')
    def hello(request):
       return {'hello': 'world'}
    • 在url.py配置url路由

    from django.contrib import admin
    from django.urls import path
    from .api import api
    
    urlpatterns = [
       path('admin/', admin.site.urls),
       path('api/', api.urls),
    ]
  • 请求方法选择

请求方法选择。如果一个方法一个处理函数,直接使用@api.get(path);

如果是多个方法一个处理函数,则使用@api.api_operation(method, path)。

其中,method用列表表示。

@api.api_operation(['GET','POST'],'/hello')  # 请求方法必须大写

路由器

多应用路由

  • 当有多个应用时,在每个应用中的创建一个api.py模块(或者直接在views.py模块)中写各自的路由.

from ninja import Router
from .models import xxx

router = Router()

@router.get("/")
def list_events(request):
    return [
        {"id": elem.id, "title": elem.title}
        for elem in xxx.objects.all()
    ]
  • 在项目文件夹urls.py中实现多个应用的router注册

from ninja import NinjaAPI 
from xxx.api import router as xxx_router
from yyy.api import router as yyy_router

api = NinjaAPI()

api.add_router("/xxx/", xxx_router)
api.add_router("/yyy/", yyy_router)

urlpatterns = [
    path("admin/", admin.site.urls),
    path("api/v1/", api.urls)
]

路由器认证

api.add_router("/xxx/", xxx_router, auth=BasicAuth())
# or we can write as this
# router = Router(auth=BasicAuth())

路由器标签

  • 可以使用tags参数将标签应用于路由器声明的操作。

api.add_router("/xxx/", xxx_router, tags=["xxx"])
# or we can write as this
# router = Router(tags=["xxx"])

路由器嵌套

from django.contrib import admin
from django.urls import path
from ninja import NinjaAPI, Router

API = NinjaAPI()

first_router = Router()
second_router = Router()
third_router = Router()

@api.get("/add")
def add(request, a: int, b: int):
    return {"result": a+ b}

@first_router.get("/add")
def add(request, a: int, b: int):
    return {"result": a+ b}

@second_router.get("/add")
def add(request, a: int, b: int):
    return {"result": a+ b}

@third_router.get("/add")
def add(request, a: int, b: int):
    return {"result": a+ b}

second_router.add_router("l3", third_router)
first_router.add_router("l2", second_router)
api.add_router("l1", first_router)

urlpatterns = [
    path("admin/", admin.site.urls),
    path("api/", api.urls),
]
# 以上路由可有以下路径
/api/add
/api/l1/add
/api/l1/l2/add
/api/l1/l2/l3/add

请求数据

路径参数

  • 所有的路径参数都会按照给定的类型自动转化,如果转化失败,则报错。

  • 常规python格式化字符串形式

# 不指定参数类型,默认为字符串
@api.get("/items/{item_id}")
def read_item(request, item_id):
    return {"item_id": item_id}

# 指定参数类型
@api.get("/items/{item_id}")
def read_item(request, item_id: int):
    return {"item_id": item_id}

路径参数转换器

@api.get("/items/{int:item_id}")
def read_item(request, item_id):
    return {"item_id": item_id}

注:{int:item_id}之间不允许有空格,有空格会报错。

使用Schema路径转换

import datetime
from ninja import Schema, Path

class PathSchema(Schema):
    year: int
    month: int
    day: int

    def value(self):
        return datetime.date(self.year, self.month, self.day)


@api.get("/events/{year}/{month}/{day}")
def events(request, date: PathSchema = Path(...))

注:Path()函数用来标记date参数是路径参数。

请求参数

请求参数分为两种:位置参数和可选参数。

位置参数

  • 不给定参数类型,则默认为字符串类型。

@api.get("/weapons")
def list_weapons(request, limit, offset):
    # type(limit) == str
    # type(offset) == str

可选参数

  • 通过给定参数默认值实现。

@api.get("/weapons")
def list_weapons(request, limit: int = 10, offset: int = 0):
    return weapons[offset:offset+limit]

位置参数和可选参数综合使用

@api.get("/weapons/search")
def search_weapons(request, keyword: str, offset: int = 0):
    results = [w for w in weapons if keyword in w.lower()]
    return results[offset: offset+10]

使用Schema定义

import datetime
from typing import List
from pydantic import Field
from ninja import Query, Schema


class Filters(Schema):
    limit: int = 100
    offset: int = None
    query: str = None
    category__in: List[str] = Field(None, alias="categories")


@api.get("/filter")
def events(request, filters: Filters = Query(...)):
    return {"filters": filters.dict()}

注:Query()函数用来标记filters参数是查询参数。

请求体

使用Schema

from ninja import Schema


class Item(Schema):
    name: str
    description: str = None
    price: float
    quantity: int


@api.post("/items")
def create(request, item: Item):
    return item

注意:如果使用None作为默认值,则该参数可传可不传。

路径参数、查询参数和请求体

from ninja import Schema

class Item(Schema):
    name: str
    description: str = None
    price: float
    quantity: int


@api.post("/items/{item_id}")
def update(request, item_id: int, item: Item, q: str):
    return {"item_id": item_id, "item": item.dict(), "q": q}
  • 三者同时出现时,解析如下:

    • 如果参数在路径中申请,则该参数为路径参数;

    • 如果参数类型申明使用单数类型(例如 int, float, str, bool等),则该参数为请求参数;

    • 如果参数类型申明使用Schema,则该参数为请求体。

Form表单

Form数据作为参数

from ninja import Form

@api.post("/login")
def login(request, username: str = Form(...), password: str = Form(...)):
    return {"username": username, "password": password}

使用Schema

from ninja import Schema, Form

class Item(Schema):
    name: str
    description: str = None
    price: float
    quantity: int

@api.post("/item")
def create(request, item: Item = Form(...)):
    return item

路径参数,请求参数和表单

from ninja import Schema, Form

class Item(Schema):
    name: str
    description: str = None
    price: float
    quantity: int


@api.post("/items/{item_id}")
def update(request, item_id: int, q: str, item: Item=Form(...)):
    return {"item_id": item_id, "item": item, "q": q}

将空表单值设置为默认值

from ninja import Schema, Form
from pydantic.fields import ModelField
from typing import Generic, TypeVar

PydanticField = TypeVar("PydanticField")

class EmptyStrToDefault(Generic[PydanticField]):
    @classmethod
    def __get_validators__(cls):
        yield cls.validate

    @classmethod
    def validate(cls, value: PydanticField, field: ModelField) -> PydanticField:
        if value == "":
            return field.default
        return value


class Item(Schema):
    name: str
    description: str = None
    price: EmptyStrToDefault[float] = 0.0
    quantity: EmptyStrToDefault[int] = 0
    in_stock: EmptyStrToDefault[bool] = True

@api.post("/item-blank-default")
def update(request, item: Item=Form(...)):
    return item.dict()

文件上传

单个文件上传

from ninja import File
from ninja.files import UploadedFile

@api.post("/upload")
def upload(request, file: UploadedFile = File(...)):
    data = file.read()
    return {"name": file.name, "len": len(data)}

多个文件上传

from typing import List
from ninja import File
from ninja.files import UploadedFile

@api.post("/upload-many")
def upload_many(request, files: List[UploadedFile] = File(...)):
    return [f.name for f in files]
  • 上传的文件属性和方法和django中相同,主要包括以下:

    • read() 从文件中读取整个上传的文件。文件太大会报错。

    • multiple_chunks(chunk_size=None) 如果上传的文件足够大,需要分块读取,返回True。默认情况下是大于2.5M的文件。

    • chunks(chunk_size=None) 一个生成器,返回文件的块。

    • name 上传文件的文件名

    • size 上传文件的大小,以字节为单位

    • content_type 与文件一起上传的内容类型头

    • content_type_extra 包含传递给content-type头的额外参数的字典。

    • charset 对于text/*内容类型,浏览器提供的字符集。

响应体Schema

一般在应用中创建一个schema.py存储。 请求相关Schema(参考请求数据) 响应体Schema

返回体为简单对象

from ninja import Schema

class UserIn(Schema):
    username: str
    password: str

class UserOut(Schema):
    id: int
    username: str

@api.post("/users/", response=UserOut)
def create_user(request, data: UserIn):
    user = User(username=data.username)
    user.set_password(data.pasword)
    user.save()
    return user

注:响应体Schema会限制返回数据,仅仅返回定义在Schema中的数据。

返回体为嵌套对象

# model.py
from django.db import models

class Task(models.Model):
    title = models.CharField(max_length=200)
    is_completed = models.BooleanFied(default=False)
    owner = models.ForeignKey("auth.User", null=True, blank=True)


# api.py
from typing import List
from ninja import Schema

class UserSchema(Schema):
    id: int
    first_name: str
    last_name: str

class TaskSchema(Schema):
    id: int
    title: str
    is_completed: bool
    owner: UserSchema = None  # None -> to mark it as optional

@api.get("/tasks", response=List[TaskSchema])
def tasks(request):
    queryset = Task.objects.all()
    return list(queryset)  # or return queryset

返回文件对象或图片

  • 文件或图片的schema均返回地址。

# model.py
class Picture(models.Model):
    title = models.CharField(max_length=100)
    image = models.ImageField(upload_to="images")

# api.py
class PictureSchema(Schema):
    title: str
    image: str

返回状态码和数据

from ninja import Schema

class Token(Schema):
    token: str
    expires: date

class Message(Schema):
    message: str

@api.post("/login", response={200: Token, 401:Message, 402:Message})
def login(request, payload: Auth):
    if auth_not_valid:
        return 401, {"message": "Unauthorized"}
    if negative_balance:
        return 402, {"message": "xxxx"}
    return 200, {"token": xx, ....}

当返回状态码和数据一致时,可使用4xx

from ninja.response import codes_1xx
from ninja.response import codes_2xx
from ninja.response import codes_3xx
from ninja.response import codes_4xx
from ninja.response import codes_5xx

@api.post('/login', response={200: Token, codes_4xx: Message})
def login(request, payload: Auth):
    if auth_not_valid:
        return 401, {'message': 'Unauthorized'}
    if negative_balance:
        return 402, {'message': 'Insufficient balance amount. Please proceed to a payment page.'}
    return 200, {'token': xxx, ...}

自我套用

class Organization(Schema):
    title: str
    part_of: 'Organization' = None

Organization.update_forward_refs()  # this is important

Model的Schema

选择包含字段

from django.contrib.auth.models import User
from ninjia import ModelSchema

class UserSchema(ModelSchema):
    class Config:
        model = User
        model_fields = ["id", "username", "first_name", "last_name"]

选择不包含字段

from django.contrib.auth.models import User
from ninja import ModelSchema

class UserSchema(ModelSchema):
    class Config:
        model = User
        model_exclude = ["password", "last_login", "user_permissions"]

覆盖字段(修改某些字段注释或者添加新字段)

class GroupSchema(ModelSchema):
    class Config:
        model = Group
        model_fields = ["id", "name"]

class UserSchema(ModelSchema):
    groups: List[GroupSchema] = []

    class Config:
        model = User
        model_fields = ["id", "username", "first_name", "last_name"]

其他,使用create_schema

  • 语法

def create_schema(
    model, # django model
    name = "",  # name for the genarated class ,if empty model name is used
    depth = 0,  # if>0 schema will be also created for nested ForeignKeys and Many2Many (with the provided depth of lookup)
    fields: list[str] = None,
    exclude: list[str] = None,
    custom_fields: list[typle[str, Any, Any]] = None  # if passed - this will override default field types (or add new fields)
)

使用model参数

注: 如未定义 schema, 仅仅使用model参数会将所有的User信息返回,容易暴露敏感数据。

from django.contrib.auth.models import User
from ninja.orm import create_schema

UserSchema = create_schema(User)
  • 使用fields参数

UserSchema = create_schema(User, fields=["id", "username"])
  • 使用exclude参数

    UserSchema = create_schema(User, exclude=["password", "last_login", ...])
  • 使用depth参数

UserSchema = create_schema(User, depth=1, fields=["username", "groups"])
# will create the following schema:
# class UserSchema(Schema):
#     username: str
#     groups: List[Group]
  • 自定义schema

参考官方文档: https://django-ninja.rest-framework.com/tutorial/config-pydantic/

认证

一般建议把认证相关可调用对象放在util包中。

使用自带认证

  • 方法一、使用django验证

from ninja import NinjaAPI
from ninja.security import django_auth

api = NinjiaAPI(csrf=True)

@api.get("/pets", auth=django_auth)
def pets(request):
    return "Authenticated user {}".format(request.auth)

访问"/pets"路由时,会使用Django会话身份验证(默认是基于cookie),验证通过调用对应视图函数,否则返回HTTP-401错误。

  • 方法二、全局认证

from ninja import NinjaAPI, Form
from ninja.security import HttpBearer

class GlobalAuth(HttpBearer):
    def authenticate(self, request, token):
        if token == "supersecret":
            return token

api = NinjaAPI(auth=GlobalAuth())

在全局认证时,如果某些函数不需要全局认证,则将该路由路径中的auth设置为None。

from ninja import NinjaAPI, Form
from ninja.security import HttpBearer

class GlobalAuth(HttpBearer):
    def authenticate(self ,request, token):
        if token == "supersecret":
            return token

api = NinjaAPI(auth=GlobalAuth())

@api.post("/token", auth=None)  # overriding global auth
def get_token(request, username: str = Form(...), password: str = Form(...)):
    if username == "admin" and password == "password":
        return {"token": "supersecret"}
  • 方法三、路由器认证

api.add_router("/events", events_router, auth=BasicAuth())
# or we can write as this
# router = Router(auth=BasicAuth())

使用自定义认证

  • 自定义功能

"auth="参数接受任何Callable对象。仅当可调用对象返回可转化为布尔值True的值时,NinjaAPI才通过身份验证。此返回值将分配给属性request.auth。

方法一、请求URL参数中带有api_key验证信息

# GET /something?api_key=abcdefg12345
from ninja.security import APIKeyQuery
from someapp.models import Client

class ApiKey(APIKeyQuery):
    param_name = "api_key"  
    def authenticate(self, request, key):
        try: 
            return Client.objects.get(key=key)
        except Client.DoesNotExist:
            pass

@api.get("/apikey", auth=ApiKey())
def apikey(request):
    assert isinstance(request.auth, Client)
    return "Hello {}".format(request.auth)

param_name是江北检查的GET参数的部分。如果未设置,将使用默认值"key"。

方法二、请求Header头中带有X-API-KEY验证信息

# GET /something HTTP/1.1
# X-API-Key: abcdef12345
from ninja.security import APIKeyHeader

class ApiKey(APIKeyHeader):
    param_name = "X-API-Key"
    def authenticate(self, request, key):
        if key == "supersecret":
            return key

@api.get("/headerKey", auth=ApiKey())
def apikey(request):
    return "Token = {}".format(request.auth)

方法三、Cookie中带有验证

# GET /something HTTP/1.1
# Cookie: X-API-KEY=abcdef12345
from ninja.security import APIKeyCookie

class CookieKey(APIKeyCookie):
    def authenticate(self, request, key):
        if key = "supersecret":
            return key

@api.get("/cookiekey", auth=CookieKey())
def apikey(request):
    return "Token = {}".format(request.auth)

方法四、HTTP JWT 验证

from ninja.security import HttpBearer

class AuthBearer(HttpBearer):
    def authenticate(self, request, token):
        if token == "supersecret":
            return token


@api.get("/bearer", auth=AuthBearer())
def bearer(request):
    return {"token": request.auth}

方法五、HTTP基本身份验证

from ninja.security import HttpBasicAuth

class BasicAuth(HttpBasicAuth):
    def authenticate(self, request, username, password):
        if username == "admin" and password == "secret":
            return username

@api.get("/basic", auth=BasicAuth())
def basic(request):
    return {"httpuser": request.auth}

方法六、多个验证器

from ninja.security import APIKeyQuery, APIKeyHeader

class AuthCheck:
    def authenticate(self, request, key):
        if key == "supersecret":
            return key

class QueryKey(AuthCheck, APIKeyQuery):
    pass

class HeaderKey(AuthCheck, APIKeyHeader):
    pass

@api.get("/multiple", auth=[QueryKey(), HeaderKey()])
def multiple(request):
    return "Token = {}".format(request.auth)

改变出现错误时返回值(自定义异常)

from ninja import NinjaAPI
from ninja.security import HttpBearer

api = NinjaAPI()

class InvalidToken(Exception):
    pass

@api.exception_handler(InvalidToken)
def on_invalid_token(request, exc):
    return api.create_response(request, {"detail": "Invalid token supplid"}, status=401)

class AuthBearer(HttpBearer):
    def authenticate(self, request, token):
        if token == "supersecret":
            return token
        raise InvalidToken

@api.get("/bearer", auth=AuthBearer())
def bearer(request):
    return {"token": request.auth}

操作参数

标签

  • OpenAPI上分组依据,默认按router分组。

  • 使用tags参数(list[str])对API操作进行分组

@api.get("/orders/", tags=["orders"])
def create_order(request, order: Order):
    return {"success": True}

路由器标签

api.add_router("/xxx/", xxx_router, tags=['xxx'])
# or we can write like this
# router = Router(tags=["xxx"])

操作:摘要

  • OpenAPI上操作的可读名称,默认为视图函数名称大写生成。

使用summary参数进行修改。

@api.get("/hello", summary="Say Hello")
def hello(request, name: str):
    return {"hello": name}

操作:说明

  • OpenAPI上显示操作的的说明信息。

@api.post("/orders", description="Create an order and updates stock")
def create_order(request, order: Order):
    return {"success": True}

注:当需要提供很长的多行描述时,可以使用 Pythondocstrings进行函数定义:

OpenAPI操作ID

OpenAPIoperationId是一个可选的唯一字符串,用于标识操作。如果提供,这些 ID 在您的 API 中描述的所有操作中必须是唯一的。

默认情况下,Django Ninja将其设置为module name+ function name。

  • 每个操作单独设置。

@api.post("/tasks", operation_id="create_task")
def new_task(request):
    ...
  • 覆盖全局行为。

from ninja import NinjaAPI

class MySuperApi(NinjaAPI):
    def get_openapi_operation_id(self, operation):
        # here  you can access operation ( .path , .view_func, etc)
        return ...

api = MySuperApi()

@api.get()
...

操作:已弃用

将操作标记为已弃用。使用deprecated参数。

@api.post("/make-order", deprecated=True)
def xxx_old_method(request, order: Order):
    return {"success": True}

输出响应选项

  • by_alias:使用应将字段别名用作响应中的键(默认为False)。

  • excluede_unset:是否应将从响应中排除在创建构架时未设置且具有默认值的字段(默认为False)。

  • exclude_defaults:是否应从响应中排除等于其默认值(无论是否设置)的字段(默认为False)。

  • exclude_none:是否从响应中排除等于None的字段(默认为False)

从架构中包含/排除操作(文档)

# 从OpenAPI模式中排除某些操作。使用include_in_schema参数。

@api.poat("/hidden", include_in_schema=False)
def xxx_hiden_operation(request):
    pass

网址名称

  • 允许设置api端点url名称(使用django路径命名)。**

@api.post("/tasks", url_name="tasks")
def xxx_operation(request):
    pass

# then you can get the url with
reverse("api-1.0.0: tasks")

版本控制

使用Django Ninja,可以轻松地从单个 Django 项目运行多个 API 版本。

不同的API版本号

# api_v1.py
from ninja import NinjaAPI

api = NinjaAPI(version="1.0.0")

@api.get("/hello")
def hello(request):
    return {"message": "Hello form v1"}
# api_v2.py
from ninja import NinjaAPI

api = NinjaAPI(version="2.0.0")

@api.get("/hello")
def hello(reqeust):
    return {"message": "Hello from v2"}
# urls.py
from api_v1 import api as api_v1
from api_v2 import api as api_v2

urlpatterns = [
    ...
    path("api/v1/", api_v1.urls),
    path("api/v2/", api_v2.urls),
]

不同的业务逻辑

...
api = NinjaAPI(auth=token_auth, urls_namespace="public_api")
...
api_private = NinjaAPI(auth=session_auth, urls_namespace="pricate_api")
...
urlpatterns = [
    ...
    path("api/", api.urls),
    path("internal-api/", api_private.urls),
]

请求解析器

在大多数情况下,REST API 的默认内容类型是 JSON,但如果您需要使用其他内容类型(如 YAML、XML、CSV)或使用更快的 JSON 解析器,Django Ninja提供了parser配置。

YAML解析器

import yaml
from typing import List
from ninja import NinjaAPI, Schema
from ninja.parser import Parser

class MyYamlParser(Parser):
    def parse_body(self, request):
        return yaml.safe_load(request.body)

api = NinjaAPI(parser=MyYamlParser())

class Payload(Schema):
    ints: List[int]
    string: str
    f: float

@api.post("/yaml")
def operation(request, payload: Payload):
    return payload.dict()

ORJSON 解析器

import orjson
from ninja import NinjaAPI
from ninja.parser import Parser

class ORJSONParser(Parser):
    def parse_body(self, request):
        return orjson.loads(request.body)

api = NinjaAPI(parser=ORJSONParser())

响应渲染器

REST API 最常见的响应类型通常是JSON。Django Ninja还支持自定义渲染器,这可以让您灵活地设计自己的媒体类型

创建渲染器

from ninja import NinjaAPI
from ninja.renderers import BaseRender

class MyRenderer(BaseRender):
    media_type = "text/plain"
    def render(self, request, data, *, response_status):
        return ... # your serialization here

api = NinjaAPI(renderer=MyRenderer())
  • render函数参数如下:

    • request: HttpRequest对象

    • data:需要序列化的对象

    • response_status(int):将返回给客户端的HTTP状态码

ORJSON 渲染实例

orjson 是一个快速、准确的 Python JSON 库。它作为 JSON 最快的 Python库进行基准测试,并且比标准json库或其他第三方库更准确。它还本机序列化数据类、日期时间、numpy 和 UUID 实例。

import orjson
from ninja import NinjaAPI
from ninja.renderers import BaseRender

class ORJSONRenderer(BaseRender):
    media_type = "application/json"
    def render(self, request, data, *, status_code):
        return orjson.dumps(data)

api = NinjaAPI(renderer=ORJSONRenderer())

XML渲染实例

将所有响应输出为XML的渲染器。

from io import StringIO
from django.utils.encoding import force_str
from django.utils.xmlutils import SimpleXMLGenerator
from ninja import NinjaAPI
from ninja.renderers import BaseRenderer

class XMLRenderer(BaserRenderer):
    media_type = "text/xml"
    def render(self, request, data, *, status_code):
        stream = StringIO()
        xml = SimpleXMLGenerator(stream, "utf-8")
        xml.startDocument()
        xml.startElement("data", {})
        self._to_xml(xml, data)
        xml.endElement("data")
        xml.endDocument()
        return stream.getvalue()

    def _to_xml(self, xml, data):
        if isinstance(data, (list, tuple)):
            for item in data:
                xml.startElement("item", {})
                self._to_xml(xml, item)
                xml.endElement("item")
        elif isinstance(data, dict):
            for key, value in data.items():
                xml.startElement(key, {})
                self._to_xml(xml, value)
                xml.endElement(key)
        elif data is None:
            pass
        else:
            xml.characters(force_str(data))

api = NinjaAPI(renderer=XMLRenderer())

错误处理

自定义异常处理

  • 定义一些异常(或使用现有的)

  • 使用 api.exception_handler 装饰器

api = NinjaAPI()

class ServiceUnavailableError(Exception):
    pass

@api.exception_handler(ServiceUnvailableError)
def service_unvailable(request, exc):
    return api.create_response(
        request,
        {"message": "Please retry later"},
        status=503
    )

@api.get("/service")
def some_operation(request):
    if random.choice([True, False]):
        raise ServiceUnavailableError()
    return {"message": "Hello"}
  • 异常处理函数有两个参数:

    • 请求:Django http请求

    • exc: 实际异常

    • 函数必须返回http响应

覆盖默认异常处理程序

默认初始化异常

  • ninja.errors.ValidationError: 当请求数据未验证时引发

  • ninja.errors.HttpError:用于从代码的任何位置抛出带有状态代码的http错误

  • django.http.Http404:Django的默认404异常

  • Exception: 应用程序的任何其他未处理的异常

覆盖默认处理程序

from ninja.errors import ValidationError
...
@api.exception_handler(ValidationError)
def validation_errors(request, exc):
    return HttpResponse("Invalid input", status_code=422)

抛出异常的HTTP响应

from ninja.errors import HttpError

@api.get("/xxx/resource")
def xxx_operation(request):
    if True:
        raise HttpError(503, "Service Unavailable. please retry later.")

CSRF

默认情况下,Django Ninja对所有操作都关闭了CSRF 。要打开它,您需要使用csrfNinjaAPI 类的参数:

from ninja import NinjaAPI

api = NinjaAPI(csrf=True)

将API与基于cookie的身份验证一起使用是不安全!

# 如果这样做,会报错。
from ninja import NinjaAPI
from ninja.security import django_auth

api = NinjaAPI(auth=django_auth)



# 必须启用csrf检查,基于cokkie的身份验证才安全。

from ninja import NinjaAPI
from ninja.security import django_auth

api = NinjaAPI(auth=django_auth, csrf=True)

异步支持

从3.1开始,Django开始支持异步视图。

  • 异步视图在以下方面更有效的工作:

  • 通过网络调用外部API;

  • 执行/等待数据库查询;

  • 从/向磁盘驱动器读取/写入

  • 快速示例

import time

@api.get("/say-after")
async def say_after(request, delay: int, word: str):
    await asyncio.sleep(delay)
    return {"saying": word}

注意:要运行此代码,必须使用Uvicorn或Daphne这样的ASGI服务器。

  • 使用Uvicorn

# 安装

pip install uvicorn

# 启动服务器

uvicorn your_project.asgi.application --reload

混合同步和异步操作

项目中可以同时使用同步和异步操作,Django Ninja会指自动路由。

@api.get("/sya-sync")
def say_after_sync(request, delay: int, word: str):
    time.sleep(delay)
    return {"saying": word}

@api.get("/say_async")
async def say_after_async(request, delay: int, word: str):
    await asyncio.sleep(delay)
    return {"saying": word}

弹性搜索示例

from ninja import NinjaAPI
from elasticsearch import AsyncElasticsearch

api = NinjaAPI()
es = AsyncElasticsearch()

@api.get("/search")
async def search(request, q: str):
    resp = await es.search(
        index = "document",
        body={"query": {"query_string": {"query": q}}},
        size=20   
    )
    return resp["hits"]

使用ORM

目前,(2020 年 7 月)Django 的某些关键部分无法在异步环境中安全运行,因为它们具有无法感知协程的全局状态。Django 的这些部分被归类为“async-unsafe”,并且在异步环境中不会被执行。ORM是主要示例,但还有其他部分也以这种方式受到保护。

如果直接用异步操作ORM,则会报错。

# 会报错
@api.get("/blog/{post_id}")
async def search(request, post_id: int):
    blog = Blog.objects.get(pk=post_id)
    ...

使用装饰器,将同步转化为异步

from asgiref.sync import sync_to_async

@sync_to_async
def get_blog(post_id):
    return Blog.objects.get(pk=post_id)

@api.get("/blog/{post_id}")
async def search(request, post_id: int):
    blog = await get_blog(post_id)
    ...

不使用装饰器

@api.get("/blog/{post_id}")
async def search(request, post_id: int):
    blog = await sync_to_async(Blog.objects.get)(pk=post_id)

不会理解执行

all_blogs = await sync_to_async(Blog.objects.all)()

会立即执行

all_blogs = await sync_to_async(list)(Blog.objects.all())
PreviousDjango 下 redis 操作NextDjango django-import-export

Last updated 2 years ago