PythonでのFastAPIの依存性注入の問題

Oct 05 2020

良い一日!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

メインとルートの構造は例と同じです https://fastapi.tiangolo.com/tutorial/bigger-applications/

ではルート/ 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()

ではハンドラ/ factory.py:

from fastapi import Depends
from .users import UserHandler1

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

ではハンドラ/ 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

次に、ハイパーコーンサーバーを実行しますapp.main:app --reload。apiメソッドを呼び出してみてください:http://127.0.0.1:8000/users/エラーAttributeErrorを取得します:'Depends' object has no attribute 'get_all'

ハンドラーレイヤーを削除してこれを行うと、すべてが機能します。

ルート/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ライブラリを使用しようとしましたが、グラフを最初に作成する必要があります。グラフにアクセスするには、グラフをどこかに保存する必要があります。

質問2(より重要):この場合、依存性注入をどのように適用できますか?

回答

1 lsabi Oct 05 2020 at 12:37

コメントに記載されているように、依存関係は呼び出し可能であり、したがってクラスでもあります。後者の場合の唯一の注意点は、クラスが初期化されるだけであるということです(つまり、init(..)関数のみが呼び出されます)。

したがって、次の例のように、依存関係としてクラスを持つために https://fastapi.tiangolo.com/tutorial/dependencies/classes-as-dependencies/#shortcutinit内でターゲット関数を呼び出し、値をクラスの属性として設定するだけです。

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

これはモデリングの質問です。ここでは、私の観点から適切だと思うものを提案します。それは必ずしも最良または最も単純なアプローチである必要はありません。

2番目の質問に答えると、このような複雑な依存関係についてはアドバイスしません。依存関係がルーターレベルにある場合は、パラメーターdepends=[...]を使用し、依存関係のクラス/関数のリストを提供して、それらをルーターに追加するだけです。

または、ファクトリの場合と同様に、すべての依存関係をエンドポイントの関数パラメーターとして宣言することもできます。この方法では、コードの大きなチャンクがコピーアンドペーストされる可能性があるため、上記のアプローチをお勧めします。

データパラメータを処理する必要がある場合は、それらをリクエストに追加し、エンドポイント内からアクセスします。最小限の例については、FastAPIがAPIキーからユーザーIDを取得するを参照してください。