FastAPI 的正确工程化方式是什么样的?

2021-02-09 17:57:45 +08:00
 LeeReamond

如题,重构项目时组织文件遇到一个循环导入问题。

通常 web 应用做法是将视图单独拿出来放在一起,路由也拿出来放在一起,等等等等,然后启动服务时从程序入口一股脑的导入。但是为了方便使用,我们希望将程序所有配置集中起来放在入口处,这样启动时就可以轻易地开关功能。

所以比如一个典型情况,在程序入口(假设名为 app.py )处我们需要定义一个 jinja2 template,这种模板文件夹的位置属于配置问题,不适合写死在业务模块里。但这些 template 又需要被业务模块所调用,比如某个路由返回某个渲染后的模板之类的。这就产生了,视图需要导入 app.py 里的内容,而 app.py 又需要导入视图才能运行,产生了循环导入的问题。

通常的 web 框架有很多变通的解决办法,比如偏函数、装饰器、或者一些共享状态组件之类的。但是 fastapi 由于注入了参数化模块,导致比如在下面的视图逻辑中

@app.get('/')
async def root(request:Request , template: jinja2.Tmeplate):
    return template.RenderResponse('index.html' , {'title':'hello world'})

root = functools.partial(template = template)

比如这种方式是行不通的,所有输入参数都会被 fastapi 拿来进行参数检查。

1193 次点击
所在节点    问与答
7 条回复
johnsona
2021-02-09 20:54:48 +08:00
fullstack-fastapi 这个仓库
LeeReamond
2021-02-10 11:05:47 +08:00
@johnsona 看了一下,还是没有解决工程问题,他这个 app=FastAPI()和视图是写在一起的。

另外看有几个人收藏这个帖子了,本着负责的态度回复一下我目前的解决方案。我由于前段时间研究了一下 PEP563,所以自然地想到运行时获取 globals 的方法,无论导入过程如何,运行时都可以比较简单地从 sys.modules 里获得实例对象。理论上这个做法不会产生安全问题、不会被 deprecated,算是一定程度上的 hack,寻址速度方面基本也可以忽略影响,目前是堪用。

但是最好还是不 hack,所以我来 v2 问一下大家的做法,但是似乎没人回复
johnsona
2021-02-10 13:02:35 +08:00
@LeeReamond 听起来有点像 flask 循环引用的问题 办法有 在 app.py 结尾引入视图。2,通过传入 app 而不是 import app 方式把 app 给视图 3 类似 flask 蓝图的东西
LeeReamond
2021-02-10 17:03:47 +08:00
@johnsona 问题在于无法传入,视图最终逻辑函数的参数是固定的,你不能引入一些与 http 无关的其他业务参数
johnsona
2021-02-10 17:15:15 +08:00
@LeeReamond
def register ( app ):
@app.route
def hello ():
return 1
不知道是否要这个
LeeReamond
2021-02-11 04:40:18 +08:00
@johnsona 你怎么把 register 和 hello()联系起来。比如我在 main.py 中有这几行代码

app=fastapi()
template=jinja2.template()

然后视图中有
@app.get()
def hello()
return template.render('index.html')
johnsona
2021-02-11 07:26:43 +08:00
@LeeReamond from xxx import register
register ( app )

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/752678

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX