Problem wstrzykiwania zależności w FastAPI w Pythonie
Dobry dzień! Proszę powiedz mi, jak możesz rozwiązać następujący problem w Pythonie + FastAPI.
Istnieje projekt testowy:
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
Struktura główna i trasy jest taka sama jak w przykładzie https://fastapi.tiangolo.com/tutorial/bigger-applications/
W pliku tours / 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()
W pliku handlers / factory.py :
from fastapi import Depends
from .users import UserHandler1
def get_handler(handler=Depends(UserHandler1)):
return handler
W 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()
repozytoria / 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
Następnie uruchamiam serwer hypercorn: app.main:app --reloadwypróbuj metodę call api: http://127.0.0.1:8000/users/i otrzymuję błąd AttributeError:'Depends' object has no attribute 'get_all'
Jeśli usuniesz warstwę obsługi i zrobisz to, wszystko będzie działać.
route / 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.
Pytanie 1: Jak używać opcji „Zależy” w tym przypadku i dlaczego nie działa?
Generalnie fabryka nie wygląda na dobre rozwiązanie tego problemu. Widziałem przykład implementacji DI z dziedziczeniem wielokrotnym, ale jak dla mnie jest to to samo, co metoda fabryczna. Próbowałem też użyć biblioteki Pinject, ale wymaga to wstępnej konstrukcji wykresu, który trzeba gdzieś zapisać, aby uzyskać do niego dostęp w programach obsługi API.
Pytanie 2 (ważniejsze): Jak w tym przypadku zastosować Dependency Injection?
Odpowiedzi
Jak zauważono w komentarzach, zależnością może być wszystko, co jest wywoływalne, a zatem także klasa. Jedynym zastrzeżeniem w tym drugim przypadku jest to, że klasa zostanie tylko zainicjowana (tzn . Zostanie wywołana tylko funkcja init (..)).
Tak więc, aby mieć klasę jako zależność, jak w przykładzie https://fastapi.tiangolo.com/tutorial/dependencies/classes-as-dependencies/#shortcutwystarczy wywołać funkcje docelowe w init i ustawić wartości jako atrybuty klasy.
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
PYTANIE 2
NB
To jest pytanie modelujące. Tutaj proponuję to, co uważam za odpowiednie z mojego punktu widzenia. Nie musi to być koniecznie najlepsze czy najprostsze podejście.
Odpowiadając na twoje drugie pytanie, nie radziłbym w przypadku tak złożonych zależności. Jeśli zależności są na poziomie routera, możesz po prostu dodać je do routera, używając parametru depends=[...]i podając listę klas / funkcji zależności.
Alternatywnie można zadeklarować wszystkie zależności jako parametry funkcji punktu końcowego, tak jak w przypadku fabryki. Ta metoda może prowadzić do kopiowania i wklejania dużych fragmentów kodu, więc radzę zastosować powyższe podejście.
Jeśli musisz przetworzyć parametry danych, dodajesz je do żądania i uzyskujesz do nich dostęp z punktu końcowego. Minimalny przykład można znaleźć w artykule FastAPI get user ID from API key .