Python 上 FastAPI 的依赖注入问题
Dependency Injection problem with FastAPI on Python
美好的一天!请告诉我如何在 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()
repos/factory.py:
from fastapi import Depends
from ..repos.user_repository import UserRepository
def get_repo(repo=Depends(UserRepository)):
return repo
repos/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')]
domain/user.py:
class User:
id: int
name: str
def __init__(self, id, name):
self.id = id
self.name = name
然后我运行 hypercorn 服务器:app.main:app --reload
尝试调用 api 方法:http://127.0.0.1:8000/users/
并得到错误 AttributeError: 'Depends' object has no attribute 'get_all'
如果您删除处理程序层并执行此操作,那么一切都会正常进行。
routes/users.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:在这种情况下如何使用“取决于”,为什么它不起作用?
总的来说,工厂看起来不是很好的解决这个问题的方法。我看到了一个使用多重继承的 DI 实现示例,但对我来说它与工厂方法相同。
我也尝试过使用 Pinject 库,但它需要初始构造图形,需要将其保存在某个地方以便在 api 处理程序中访问它。
问题 2(更重要):在这种情况下如何应用依赖注入?
如评论中所述,依赖项可以是任何可调用的内容,因此也可以是 class。在后一种情况下唯一需要注意的是 class 只会被初始化(即只会调用 __init__(...)
函数)。
所以,为了有一个 class 作为依赖,就像在 https://fastapi.tiangolo.com/tutorial/dependencies/classes-as-dependencies/#shortcut 的例子中一样,你只需要在 init 并将值设置为 class.
的属性
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
NB
This is a modelling question. Here I propose what I believe is
suitable from my point of view. It does not necessarily have to
be best or simplest approach.
回答你的第二个问题,对于如此复杂的依赖关系,我不建议。如果依赖关系在路由器级别,您可以简单地将它们添加到路由器,使用参数 depends=[...]
并提供依赖关系列表 classes/functions.
或者,您可以将所有依赖项声明为端点的函数参数,就像您对工厂所做的那样。这种方法可能会导致大块代码被复制和粘贴,所以我建议采用上述方法。
如果您需要处理数据参数,则将它们添加到请求中并从端点内访问它们。有关最小示例,请参阅 。
__call__
方法必须在 class 中实现。
美好的一天!请告诉我如何在 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()
repos/factory.py:
from fastapi import Depends
from ..repos.user_repository import UserRepository
def get_repo(repo=Depends(UserRepository)):
return repo
repos/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')]
domain/user.py:
class User:
id: int
name: str
def __init__(self, id, name):
self.id = id
self.name = name
然后我运行 hypercorn 服务器:app.main:app --reload
尝试调用 api 方法:http://127.0.0.1:8000/users/
并得到错误 AttributeError: 'Depends' object has no attribute 'get_all'
如果您删除处理程序层并执行此操作,那么一切都会正常进行。
routes/users.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:在这种情况下如何使用“取决于”,为什么它不起作用?
总的来说,工厂看起来不是很好的解决这个问题的方法。我看到了一个使用多重继承的 DI 实现示例,但对我来说它与工厂方法相同。 我也尝试过使用 Pinject 库,但它需要初始构造图形,需要将其保存在某个地方以便在 api 处理程序中访问它。
问题 2(更重要):在这种情况下如何应用依赖注入?
如评论中所述,依赖项可以是任何可调用的内容,因此也可以是 class。在后一种情况下唯一需要注意的是 class 只会被初始化(即只会调用 __init__(...)
函数)。
所以,为了有一个 class 作为依赖,就像在 https://fastapi.tiangolo.com/tutorial/dependencies/classes-as-dependencies/#shortcut 的例子中一样,你只需要在 init 并将值设置为 class.
的属性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
NB
This is a modelling question. Here I propose what I believe is suitable from my point of view. It does not necessarily have to be best or simplest approach.
回答你的第二个问题,对于如此复杂的依赖关系,我不建议。如果依赖关系在路由器级别,您可以简单地将它们添加到路由器,使用参数 depends=[...]
并提供依赖关系列表 classes/functions.
或者,您可以将所有依赖项声明为端点的函数参数,就像您对工厂所做的那样。这种方法可能会导致大块代码被复制和粘贴,所以我建议采用上述方法。
如果您需要处理数据参数,则将它们添加到请求中并从端点内访问它们。有关最小示例,请参阅
__call__
方法必须在 class 中实现。