FastAPI基础:Depends怎么用?
目录
前言
一、Depends的是干什么的?
二、Depends的两种用法
1、Depends(function)形式
2、Depends(class)形式
三、拓展
前言
FastAPI真的是个非常好用的东西。首先它是异步,但是我想说的是Fast API对于保证数据交互传递过程中的一致性,保持的非常好。
当然,能做到这一点也是因为它站在了巨人肩膀上,因为它的schema是依靠pydantic来做的。
大家用Fast API的过程中会越来越发现,Fast API实现的东西并不多,它大部分的功能都是基于其他工具的(如pydantic、startllete)。
今天想记录一下Fast API中实现的一个非常好用的工具——Depends,它能帮我们减少很多的代码。当然,我也是看官方文档然后结合自己的理解来写这篇文章,小伙伴们如果有精力还是建议去看官方文档,Fast API的官方文档写的很通俗易懂的。
一、Depends的是干什么的?
我们知道Fast API对于路径参数(path parameter)、请求参数(Query parameters)、请求体参数(body parameters)有很明确的区别。简单的说一下,
- path parameters 也就是你在路径里怎么传参,如 www.baidu.com/image/baike?id=001 这种,你想把image作为参数传,那么你要写成 www.baidu.com/{type}/
- Query parameters 可以当成是get请求中的查询参数,如 www.baidu.com/image/baike?id=001 ,其中id=001 就是查询参数
- body parameters 默认是json形式传参,用于post请求中的请求体,使用继承BaseModel的类进行schema
而我们今天要说的Depends,其实就是对于Query parameters的schema
为什么要Depends呢?大家看完fastAPI 用于post请求的body parameters的schema参数,是不是觉得这种schema方式很好,只要提供一个类,就可以完成传参的校验,并且还可以在Fast API的docs可视化文档中形成参数提示。也增加了代码的可重复利用性,减少了代码冗余。
那么,类比过来,对于get请求的Query parameters,我们是不是也希望有这么一种方式,可以写一个类,然后就可以自动解析url中的参数,并且进行schema,还在docs文档中生成提示?
对了,Depends可以完美完成,并且Depends还不止这些作用,还可以用来提供数据库连接、外部api连接等等,我们后面接着看吧。
二、Depends的两种用法
1、Depends(function)形式
记住一点:我们管Depends这种方式,叫做 Dependency Injection。当然内部Fastapi做了很多工作。但是请记住Dependency Injection,对于我们理解很有用处。
引用官方文档一个例子,如下:
from typing import Optionalfrom fastapi import Depends, FastAPIapp = FastAPI()async def common_parameters(q: Optional[str] = None, skip: int = 0, limit: int = 100):return {"q": q, "skip": skip, "limit": limit}@app.get("/items/")
async def read_items(commons: dict = Depends(common_parameters)):return commonsif __name__ == '__main__':uvicorn.run(app, host="0.0.0.0", port=8080, loop="asyncio")
我们看到了Depends的使用,它接收了一个参数,是一个function。这个function里定义了一些参数。那么我们运行起来,看看这个接口有什么不同?打开http://127.0.0.1:8080/docs连接,查看可视化文档。
我们找到了这个接口,看看它的chema有什么?
多了三个参数query参数 q、skip、limit,那么他们是从哪里来的呢?
没错,就是从Depends(common_parameters)这里来的。并且,由于我们设置了默认值,所以是否对q、skip、limit传参是可选的。我们点击Execute,如下:
看Request URL,证明了是把 Depends(common_parameters)当成Query parameters来使用的。
那么参数被怎么使用了呢?这个你就不用担心了,因为FastAPI已经帮你做了参数解析,什么意思呢?还记得:commons: dict = Depends(common_parameters)这句话吧,
意思就是:当有人访问http://127.0.0.1:8080/items?skip=0&limit=100这种地址时,后台会自动将自动把skip=0&limit=100这段解析出来,并赋值给commons参数,由于function返回的是一个dict,所以commons也是一个dict,我们就可以通过common.get(limit)得方式去访问这些传入的参数了。当然function也可以返回tuple、list等形式,那么commons就是对应的类型。
那么,可以像这种形式访问吗? http://127.0.0.1:8080/items 答案是可以的。为什么,因为我们在function: common_parameters 中都给参数设置了默认参数,所以可以使用默认参数。下面我们来验证一下,使用浏览器访问http://127.0.0.1:8080/items,如下所示:
正确的返回。
那么,如果我在function中设置一个必传参数呢?如下代码。首先我们看一下docs可视化文档有什么变化。如下,多了一个required的参数desc,也就是说这个是必须输入的。
那我们使用浏览器访问http://127.0.0.1:8080/items ,我就是不传desc,你能把我咋滴。如下,报错了,说的是没有传递desc参数。
那我给desc传递一个str类型的参数呢?报错了,提示说要传bool值。
通过以上的分析,我们就很明确Depends的方式,诠释了Dependency Injection的含义。真的是很好用。今后有时间了要看看Depends的源码,学习一下别人的实现方式,再补充到这篇文章。
段官方文档说明下Depends为什么可以完成注入,主要是以下3个步骤:
- 首先查看是否有dependence的使用,如果有,调用 depedency function,从URL中解析出要使用的参数传给 depedency function
- 获取depedency function的result,也就是return的值
- 将获取的dict值注入到 路径参数 中
以上三步,也说明了为什么我们写在Depends(function)里的参数可以进入到路径参数中。
所以后台得到的路径参数是,前端传递的url路径参数,经过Depends加工后传递给后台路径函数的。
另外,Depends(function) 的function要写成同步的还是异步的,其实都可以。
2、Depends(class)形式
其实使用Depends(function)这种形式有一些缺陷。这里再贴一下我们前面的代码,使用Depends(function)形式。从可读性角度,这种方式适合于参数比较容易被理解其含义的情景。
为什么?因为这种形式ID E并不能给你提供提示(hint)。
from typing import Optionalfrom fastapi import Depends, FastAPIapp = FastAPI()async def common_parameters(q: Optional[str] = None, skip: int = 0, limit: int = 100):return {"q": q, "skip": skip, "limit": limit}@app.get("/items/")
async def read_items(commons: dict = Depends(common_parameters)):return commonsif __name__ == '__main__':uvicorn.run(app, host="0.0.0.0", port=8080, loop="asyncio")
而class形式的传参其实是对function的一种升级,为什么这么说呢?,比如,我要实现一个参数相对复杂的Depends,比如Person,有name,age,height、sex等等。那么使用class的形式就比较好,会有hint提示,并且还可以使用 属性访问形式来访问元素。
但是,大家有没有想过,为什么Depends接收class也行呢?
官网上说:Depends可以接收任何callable的对象。 但是是不是落下一点,你这个callable对象得有返回值吧?就像我们上面的common_parameters函数一样。我们可以试试没有返回值,是什么样的。代码如下。
from typing import Optionalfrom fastapi import Depends, FastAPIapp = FastAPI()async def common_parameters(q: Optional[str] = None, skip: int = 0, limit: int = 100):pass@app.get("/items/")
async def read_items(commons= Depends(common_parameters)):return commonsif __name__ == '__main__':uvicorn.run(app, host="0.0.0.0", port=8080, loop="asyncio")
查看docs,我们发现校验的参数不变,唯一变化的就是async def read_items(commons= Depends(common_parameters)) 里面的commons是None。能想通,因为commons得到的是Depends(function)中function的返回值嘛。你返回None,可不commons就为None了。
所以Dpends接收的cllable对象有没有返回值都行。
但是这样commons这个参数就没意义了不是,所以我认为,从可用的角度上改成下面这句话:
” Depends接收任何有返回值的callable对象 “ 比较合适。
ok,我们使用Depends(class)的形式实现一把,看看有啥发现,代码如下:
import uvicorn
from fastapi import Depends, FastAPI
from typing import Optionalfrom pydantic import BaseModelapp = FastAPI()class CommonQueryParams:def __init__(self, q: Optional[str] = None, skip: int = 0, limit: int = 100):self.q = qself.skip = skipself.limit = limit@app.get("/items")
async def read_items(common=Depends(CommonQueryParams)):return commonif __name__ == '__main__':uvicorn.run(app, host="0.0.0.0", port=8080, loop="asyncio")
我们来看看docs有啥变化不,如下。可以正常访问,并没有啥变化。
但是,从后台使用common时,common就时一个CommonQueryParams的类型了。并且可以使用common.limit的这种属性访问形式访问元素了。
三、拓展
那么,大家有没有认真想过这句话, “的Depends可以接收任何的callable对象”。
为什么function和class都可以作为Depends参数,因为他们可以调用,即他们可以写成A()的形式。那么理论上,一个拥有__call__方法的对象也是可以传入Depends的。我们来试试。代码如下
import uvicorn
from fastapi import Depends, FastAPI
from typing import Optionalfrom pydantic import BaseModelapp = FastAPI()class CombineWord:def __init__(self, q: Optional[str] = None):self.q = qdef __call__(self):return self.q + "world"@app.get("/items")
async def read_items(word=Depends(CombineWord(q="hello"))):return wordif __name__ == '__main__':uvicorn.run(app, host="0.0.0.0", port=8080, loop="asyncio")
运行起来后,我们从docs来访问,如下,运行成功,并且返回了要拼写的字符串。
所以,大家可以根据自己的需要来给Depends传参,非常灵活。
四、实际项目中的应用-Depends(database_connection)
在实际项目中,后端开发时,用到的比较多的Depends比较多的是3个场景:
- Depends(current_user) 在路由函数中,经常需要知道当前的用户是谁,用一个def get_current_user()方法实现获取,然后Depends(current_user)
- Depends(database) 创建数据库session,特别常用,使用def connecton()获取数据库session,然后Depends(connection),看举例代码
- Depends(common_params) 有一些公共参数,可以提取出来,然后使用Depends(common_params)
1、Depends(databse)来获取数据库seesion
# db.py用于初始化数据库
# 执行db.init(config)import logging
from urllib.parse import quote_plusfrom sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, scoped_sessioncontext = {"db_url": None,"engine": None,"smaker": None
}scoped_context = {"db_url": None,"engine": None,"smaker": None
}logs = logging.getLogger("sqlalchemy")def init(config):db_url, engine, smaker = make_context(config)logs.debug("try to crate all database objects")Base.metadata.create_all(engine) # 创建所有数据表context["db_url"] = db_urlcontext["engine"] = enginecontext["smaker"] = smakerscoped_context["db_url"] = db_urlscoped_context["engine"] = enginescoped_context["smaker"] = scoped_session(smaker)def make_context(c):logs.debug("database config: %s", c)db_url = make_url(c)engine = create_engine(db_url,encoding=c.get("encoding" "utf8"),echo=c.get("echo", False),echo_pool=c.get("echo_pool", False),pool_recycle=c.get("pool_recycle", 3600),pool_size=c.get("(pool_size", 256))smaker = sessionmaker(bind=engine, expire_on_commit=c.get("expire_on_commit", False))logs.debug("create session maker")return db_url, engine, smakerdef make_url(c):protocol = c["protocol"]username = c["username"]password = c["password"]password = quote_plus(password)host = c["host"]port = c["port"]database = c["database"]template = "{}://{}:{}@{}:{}/{}"db_url = template.format(protocol, username, password, host, port, database)url_tip = template.format(protocol, username, "XXXXXX", host, port, database)logs.debug(f"create db engine to {url_tip}")return db_urldef make_session(ctx=None, scoped=False):c = ctx if ctx is not None else (scoped_context if scoped else context)m = c["smaker"]s = m()return s@contextlib.contextmanager
def session_context(ctx=None, scoped=False):session = make_session(ctx, scoped)try:yield sessionsession.commit()except Exception:session.rollback()raisefinally:session.colse()def connection():with session_context() as session:yield session
import uvicorn
from fastapi import Depends, FastAPIapp = FastAPI()@app.get("/items")
async def read_items(database=Depends(connection)):# 这里的connection就是一个mysql的session连接了data = database.query("selext xxx from yyy")return data@app.get("/users")
async def read_items(database=Depends(connection)):# 这里的connection就是一个mysql的session连接了data = database.query("selext xxx from user")return dataif __name__ == '__main__':uvicorn.run(app, host="0.0.0.0", port=8080, loop="asyncio")
每次需要使用数据库操作时,就很方便了。
2、Depends(current_user) 以后再举例子吧
FastAPI基础:Depends怎么用?相关推荐
- 2021-10-22 学习笔记:FastAPI基础使用指南
2021-10-22 学习笔记:FastAPI基础使用指南 已经第三针疫苗了,祝所有人平安! 简单使用 路径参数 查询参数 请求体 查询参数和字符串校验 路径参数和数值校验 请求体参数 请求体 -- ...
- 【FastAPI基础】17、文件操作
引言: 最近工作中有机会接触FastAPI这个框架,所以就把官方文档看了一遍,对框架的各个特性及使用方法做了总结,会逐步的发出来,希望对您有用. 如果您之前接触过python的其他框架,看起来会非常简 ...
- fastapi 基础使用
官网地址:https://github.com/tiangolo/fastapi 在项目目录下面构建一个main.py的文件,文件中存在下述内容: from fastapi import FastAP ...
- 干货:使用Fastapi开发自己的Mock server(附源码)
最近几天抽空看了下Fastapi的文档,顺便使用它开发了个HTTP(s)协议的Mock server.以后有需要用到挡板的地方,就可以直接使用啦. 项目一级目录: logs用来存放日志文件,日切 ma ...
- Python-web框架 fastapi
目录 简介 性能 api 文档 类型检查 蓝图APIRouter 自定义的tags与responses 注册 APIRouter 简介 官网教程(中文版):FastAPI python语言下的Web框 ...
- FastAPI 教程翻译 - 用户指南 26 - 安全性
FastAPI 教程翻译 - 用户指南 26 - 安全性 FastAPI Tutorial - User Guide - Secuity Security Intro 安全性简介 There are ...
- 【FastAPI后台API 八】JWT token认证登陆
JWT token认证登陆 前一篇博客讲述了获取和验证请求参数, 这一篇就实践下,演示一个最基础的JWT认证,我公司是用了两个token方式验证,一个请求token,一个刷新token,请求token ...
- 三分钟了解 Python3 的异步 Web 框架 FastAPI
快速编码,功能完善.从启动到部署,实例详解异步 py3 框架选择 FastAPI 的原因. FastAPI 介绍 FastAPI 与其它 Python-Web 框架的区别 在 FastAPI 之前,P ...
- fastapi vue socket 从其他文件调用 socket 方法
需求:因为项目需要,边做边学python,这次需要使用socket功能,正常在main.py中写个socket,还是OK的,但是我想要在其他文件中,直接使用socket的emit方法,需要在文件结构上 ...
- FastAPI 结合 SQLAlchemy 操作 MySQL 数据库
文章目录 1. 安装 SQLAlchemy 2. 创建数据库 3. SQLAlchemy 连接 MySQL 4. 创建数据模型 5. 创建 Pydantic 模型 6. crud 工具 7. main ...
最新文章
- 在CentOS 6.8上安装Nginx
- static方法与非static方法是否可以互相调用
- Unity3D与Leap motion连接学习记录
- ros轮式小车学习链接
- poi word插入图片_豌豆BI工具看板Word导出技术详解
- Xcode9 之 Xcode Server 持续集成
- [bzoj1011] [HNOI2008]遥远的行星
- 你想要的江湖,可能不在这时候笑傲
- 常用JS库源码 - store.js源码/underscore.js源码
- 七牛php连麦,七牛IOS连麦,主播端无法采集声音
- [ARC061E]すぬけ君の地下鉄旅行 / Snuke's Subway Trip
- 14-----表单输入绑定
- paip.读取WEB.XML中的参数值总结
- java 偏向锁的撤销_源码解析-偏向锁撤销流程解读
- Android UI 自定义控件大全
- 数据结构 实验2——表达式求值
- STM32F429-Discovery 编译 uclinux
- autoit v3安装
- 被雅虎3000万美金收购的Summly没有核心技术!
- 《百面机器学习》学习笔试之模型评估(第2章)