Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/database/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 在FastAPI中使用DB依赖项而无需通过函数树_Python_Database_Sqlalchemy_Fastapi - Fatal编程技术网

Python 在FastAPI中使用DB依赖项而无需通过函数树

Python 在FastAPI中使用DB依赖项而无需通过函数树,python,database,sqlalchemy,fastapi,Python,Database,Sqlalchemy,Fastapi,我目前正在复杂系统上使用FastAPI开发POC。这个项目在业务逻辑上很繁重,完成后将与50多个不同的数据库表交互。每个模型都有一个服务,一些更复杂的业务逻辑有自己的服务(然后通过特定于模型的服务与不同的表进行交互/查询) 虽然一切正常,但我的团队中的一些成员对会话对象的依赖注入提出了一些异议。最大的问题主要是必须将会话从控制器传递到服务,再传递到第二个服务和(在少数情况下)第三个服务。在这些情况下,中间服务函数往往没有数据库查询,但它们在其他服务上调用的函数可能有一些查询。抱怨主要在于这更难维

我目前正在复杂系统上使用FastAPI开发POC。这个项目在业务逻辑上很繁重,完成后将与50多个不同的数据库表交互。每个模型都有一个服务,一些更复杂的业务逻辑有自己的服务(然后通过特定于模型的服务与不同的表进行交互/查询)

虽然一切正常,但我的团队中的一些成员对会话对象的依赖注入提出了一些异议。最大的问题主要是必须将会话从控制器传递到服务,再传递到第二个服务和(在少数情况下)第三个服务。在这些情况下,中间服务函数往往没有数据库查询,但它们在其他服务上调用的函数可能有一些查询。抱怨主要在于这更难维护,而且必须到处传递DB对象似乎是无用的重复

示例代码:

databases/mysql.py(项目中3个数据库之一)

从sqlalchemy导入创建引擎
从sqlalchemy.ext.declarative导入声明性基础
从sqlalchemy.orm导入sessionmaker,会话
def get_uri():
返回“mysql uri”
引擎=创建引擎(获取uri())
SessionLocal=sessionmaker(autocommit=False,autoflush=False,bind=engine)
Base=声明性_Base()
def get_db():
db:Session=SessionLocal()
尝试:
收益率分贝
db.commit()
除例外情况外:
db.rollback()
最后:
db.close()
控制器/控制器1.py

从fastapi导入APIRouter,HTTPException,路径,取决于
从sqlalchemy.orm导入会话
从services.mysql.bar导入按\u id获取\u bar\u
从services.mysql.process\u x导入bar\u进程
从databases.mysql导入get_db
路由器=APIRouter(前缀='/foo')
@get('/bar/{bar_id}'))
def process_bar(bar_id:int=Path(…,title='待处理条的id',ge=1),
mysql_session:session=dependens(get_db)):
#从crontroller到只运行查询的服务。这很好。
bar=get\u bar\u by\u id(bar\u id,mysql\u会话)
如果bar为“无”:
引发HTTPException(状态代码=404,
detail='Bar未找到id:{Bar\u id}'。格式(Bar\u id=Bar\u id))
#这个函数调用了一个服务中的函数,该服务有很多业务逻辑,但没有查询
已处理的\u bar=bar\u进程(bar,mysql\u会话)
返回已处理的U形条
services/mysql/process_x.py

from.process\u-bar导入过程\u-bar
从models.mysql.w导入
从models.mysql.bar导入栏
从models.mysql.y导入
从models.mysql.z导入
从sqlalchemy.orm导入会话
def w_进程(w:w,mysql_会话:会话):
...
def bar_进程(bar:bar,mysql_会话:会话):
#非常简单,这里实际上有5个条件分支服务调用
返回进程(bar,mysql会话)
定义y_进程(y:y,mysql_会话:会话):
...
def z_进程(z:z,mysql_会话:会话):
...
services/mysql/process_bar.py

来自。导入模型_服务1
从…起导入模型_服务2
从…起导入模型_服务3
从…起导入其他\u规则\u服务
从libraries.bar_函数将do_thing_导入到_bar
从models.mysql.bar导入栏
从sqlalchemy.orm导入会话
def process___条(条:条,mysql_会话:会话):
过程\结果=列表()
#许多处理步骤,并非所有步骤都需要db,可能直接在条形图上工作
process_result.append(process1(bar,mysql_会话))
process_result.append(process2(bar,mysql_会话))
process_result.append(process3(bar,mysql_会话))
进程\结果追加(进程4(条))
处理结果追加(…(条形))
进程\结果追加(进程(条))
def process1(bar:bar,mysql_会话:会话):
返回model_service1.do_something(bar.val,mysql_会话)
def process2(bar:bar,mysql_会话:会话):
返回model_service2.do_something(bar.val,mysql_会话)
def process3(bar:bar,mysql_会话:会话):
返回model_service3.do_something(bar.val,mysql_会话)
def process4-Y(条形图:条形图,mysql_会话:会话):
#使用bar库,或者在没有查询的其他服务上做一些事情
返回列表()
正如您所看到的,在使用这种方法时,我们一直在通过mysql_会话,并让它在任何地方重复

以下是我想到的两个解决方案:

  • 将DB会话添加到Starlette请求状态
  • 我可以通过app.startup事件()或中间件来实现这一点。但是,它确实意味着以类似的方式来回传递请求状态(如果我对它的理解是正确的)

  • 使用上下文管理器的会话范围方法
  • 基本上,我会将get_db函数转换为上下文管理器,而不是将其作为依赖项注入。然而,这是迄今为止最干净的最终结果,它完全违背了在整个请求中共享单个db会话的概念

    我考虑过使用encode/databases的完全异步方法,如FastAPI文档()所示,但是我们正在处理的SqlAlchemy数据库之一是通过插件使用的,我假设它不支持异步开箱即用(Vertica)。如果我错了,那么我可以考虑完全异步的方法。


    因此,最后,我想知道的是,是否有可能在不影响每个请求的单个会话方法的情况下实现“更干净”的任务?

    我直接从

    正如用户内部提到的,我希望通过使用来实现。我已经在我的代码中尝试过了,它似乎工作得很好