首页 > 解决方案 > Python 上 FastAPI 的依赖注入问题

问题描述

再会!请告诉我如何在 Python + FastAPI 中解决以下问题。

有一个测试项目:

app / main.py - main file
app / routes / users.py -set of api methods
app / repos / factory.py - repository factory
app / repos / user_repository.py - repositories
app / handlers / factory.py - handler factory
app / handlers / users.py - handlers
app / domain / user.py - data class

main 和 routes 结构与示例中的相同https://fastapi.tiangolo.com/tutorial/bigger-applications/

routes/users.py 文件中

from fastapi import APIRouter, Depends
from ..handlers import factory

router = APIRouter()

@router.get("/users/", tags=["users"])
def read_users(handler=Depends(factory.get_handler)):
    return handler.get_all()

handlers/factory.py中:

from fastapi import Depends
from .users import UserHandler1

def get_handler(handler=Depends(UserHandler1)):
    return handler

handlers/users.py 中

from fastapi import Depends
from ..repos import factory

class UserHandler1:
    def __init__(self):
        pass

    def get_all(self, repo=Depends(factory.get_repo)):
        return repo.get_all()

回购/factory.py

from fastapi import Depends
from ..repos.user_repository import UserRepository

def get_repo(repo=Depends(UserRepository)):
    return repo

存储库/user_repository.py

from ..domain.user import User

class UserRepository:
    def __init__(self):
        pass

    def get_all(self):
        return [User(1, 'A'), User(2, 'B'), User(3, 'C')]

域/user.py

class User:
    id: int
    name: str

    def __init__(self, id, name):
        self.id = id
        self.name = name

然后我运行 hypercorn server: app.main:app --reload Try call api method:http://127.0.0.1:8000/users/ 并得到错误 AttributeError:'Depends' object has no attribute 'get_all'

如果您删除处理程序层并执行此操作,那么一切都会正常工作。

路线/用户.py:

from fastapi import APIRouter, Depends
from ..repos import factory

router = APIRouter()

@router.get("/users/", tags=["users"])
def read_users(repo=Depends(factory.get_repo)):
    return repo.get_all()
It also works if you completely remove all Depends and create
UserRepository and UserHandler1 directly in factories.

问题1:在这种情况下如何使用“Depends”,为什么它不起作用?

一般来说,工厂看起来并不能很好地解决这个问题。我看到了一个使用多重继承实现 DI 的示例,但对我来说它与工厂方法相同。我也尝试使用 Pinject 库,但它需要初始构建图形,需要将其保存在某个地方以便在 api 处理程序中访问它。

问题 2(更重要):在这种情况下如何应用依赖注入?

标签: pythondesign-patternsdependency-injectionfastapi

解决方案


如评论中所述,依赖项可以是可调用的任何内容,因此也可以是类。在后一种情况下唯一需要注意的是类只会被初始化(即只有__init__(...)函数会被调用)。

因此,为了将类作为依赖项,如https://fastapi.tiangolo.com/tutorial/dependencies/classes-as-dependencies/#shortcut的示例所示,您只需在init中调用目标函数并将值设置为类的属性。

from ..domain.user import User

class UserRepository:
    def __init__(self):
        self.get_all()

    def get_all(self):
        self.users = [User(1, 'A'), User(2, 'B'), User(3, 'C')]


from fastapi import Depends
from ..repos.user_repository import UserRepository

def get_repo(repo=Depends(UserRepository)):
    print(repo.users) # This will print the list of users
    return repo

问题2

注意

这是一个建模问题。在这里,我提出我认为从的角度来看是合适的。它不一定是最好或最简单的方法。

回答你的第二个问题,我不建议这种复杂的依赖关系。如果依赖关系在路由器级别,您可以简单地将它们添加到路由器,使用参数depends=[...]并提供依赖类/函数的列表。

或者,您可以将所有依赖项声明为端点的函数参数,就像您为工厂所做的那样。这种方法可能会导致大量代码被复制和粘贴,所以我建议采用上述方法。

如果您需要处理数据参数,则将它们添加到请求中并从端点内访问它们。有关最小示例,请参阅FastAPI 从 API 密钥获取用户 ID 。


推荐阅读