Masalah Dependency Injection dengan FastAPI pada Python
Selamat siang! Tolong beritahu saya bagaimana Anda dapat memecahkan masalah berikut dengan Python + FastAPI.
Ada proyek uji:
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
Struktur utama dan rute sama seperti pada contoh https://fastapi.tiangolo.com/tutorial/bigger-applications/
Di file 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()
Di handlers / factory.py :
from fastapi import Depends
from .users import UserHandler1
def get_handler(handler=Depends(UserHandler1)):
return handler
Di 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
Kemudian saya menjalankan server hypercorn: app.main:app --reloadCoba metode api panggilan: http://127.0.0.1:8000/users/Dan dapatkan kesalahan AttributeError:'Depends' object has no attribute 'get_all'
Jika Anda menghapus lapisan penangan dan melakukan ini, maka semuanya akan bekerja.
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.
Pertanyaan 1: Bagaimana cara menggunakan "Tergantung" dalam kasus ini dan mengapa tidak berhasil?
Secara umum, pabrik sepertinya bukan solusi yang baik untuk masalah ini. Saya melihat contoh implementasi DI menggunakan multiple inheritance tetapi bagi saya itu sama dengan metode pabrik. Saya juga mencoba menggunakan pustaka Pinject, tetapi membutuhkan konstruksi awal grafik, yang perlu disimpan di suatu tempat untuk mengaksesnya di penangan api.
Pertanyaan 2 (lebih penting): Bagaimana Injeksi Ketergantungan dapat diterapkan dalam kasus ini?
Jawaban
Seperti disebutkan di komentar, dependensi bisa berupa apa saja yang bisa dipanggil dan dengan demikian juga menjadi kelas. Satu-satunya peringatan dalam kasus terakhir adalah bahwa kelas hanya akan diinisialisasi (yaitu hanya fungsi init (..) yang akan dipanggil).
Jadi, untuk memiliki kelas sebagai ketergantungan, seperti pada contoh https://fastapi.tiangolo.com/tutorial/dependencies/classes-as-dependencies/#shortcutAnda hanya perlu memanggil fungsi target di dalam init dan menetapkan nilai sebagai atribut kelas.
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
PERTANYAAN 2
NB
Ini adalah pertanyaan pemodelan. Di sini saya mengusulkan apa yang saya yakini cocok dari sudut pandang saya . Ini tidak harus merupakan pendekatan terbaik atau paling sederhana.
Menjawab pertanyaan kedua Anda, saya tidak akan menyarankan untuk ketergantungan yang begitu rumit. Jika dependensi berada di level router, Anda cukup menambahkannya ke router, menggunakan parameter depends=[...]dan memberikan daftar kelas / fungsi dependensi.
Alternatifnya, Anda dapat mendeklarasikan semua dependensi sebagai parameter fungsi titik akhir, seperti yang Anda lakukan di pabrik. Metode ini dapat menyebabkan potongan besar kode disalin dan ditempel, jadi saya menyarankan untuk pendekatan di atas.
Jika Anda perlu memproses parameter data, Anda dapat menambahkannya ke permintaan dan mengaksesnya dari dalam endpoint. Lihat FastAPI mendapatkan ID pengguna dari kunci API untuk contoh minimal.