依赖注入与控制翻转
项目地址:
https://github.com/ets-labs/python-dependency-injector
dependency-injector==4.41.0
fastapi==0.95.0
uvicorn==0.21.1
SQLAlchemy==2.0.9
__init__.py
import os
PROJECT_ENV = os.environ.get("PROJECT_ENV", "beta")
if PROJECT_ENV == "online":
from .online import *
else:
from .beta import *
class Settings(BaseSettings):
db_conf: DBConfig = DBConfig()
beta.py
from pydantic import BaseSettings, Field
class DBConfig(BaseSettings):
url: str = Field(env="aws_ac",default="sqlite:///./webapp.db")
online.py
from pydantic import BaseSettings, Field
class DBConfig(BaseSettings):
url: str = Field(env="sqlite:///./webapp.db")
ApplicationModule.py
from dependency_injector import containers, providers
from src.api.users.module import UserModule
# from settings import Settings
# from src.database.db import Database
class Application(containers.DeclarativeContainer):
# config = providers.Configuration(pydantic_settings=[Settings()])
# db = providers.Singleton(Database, db_url=config.db_conf.url)
userModule = providers.Container(
UserModule,
)
entities/user.py
from sqlalchemy import Column, String, Boolean, Integer
from src.database.db import Base
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True)
email = Column(String, unique=True)
hashed_password = Column(String)
is_active = Column(Boolean, default=True)
def __repr__(self):
return f"{self.id}, " \
f"email=\"{self.email}\", " \
f"hashed_password=\"{self.hashed_password}\", " \
f"is_active={self.is_active})>"
controller.py
from fastapi import APIRouter, Depends
from dependency_injector.wiring import Provide, inject
from src.api.users.module import UserModule
from src.api.users.service import UserService
router = APIRouter()
@router.get("/users")
@inject
def main(
user_service: UserService = Depends(Provide[UserModule.user_service]),
) :
user = user_service.get_all_user()
return user
dao.py
from contextlib import AbstractContextManager
from typing import Iterator, Callable
from sqlalchemy.orm import Session
from src.api.users.entities.user import User
class UserDao:
def __init__(self, session_factory: Callable[..., AbstractContextManager[Session]]) -> None:
self.session_factory = session_factory
# def get_user_all(self):
# # self.db.get().all
# return {"User": 123}
def get_user_all(self) -> Iterator[User]:
with self.session_factory() as session:
return session.query(User).all()
module.py
from dependency_injector import containers, providers
from settings import Settings
from src.api.users.dao import UserDao
from src.api.users.serializer import UserSerializer
from src.api.users.service import UserService
from src.database.db import Database
class UserModule(containers.DeclarativeContainer):
# config = providers.Configuration(yaml_files=["config.yml"]) # 使用yml文件作为配置
config = providers.Configuration(pydantic_settings=[Settings()])
db = providers.Singleton(Database, db_url=config.db_conf.url)
wiring_config = containers.WiringConfiguration(modules=["src.api.users.controller"])
user_dao = providers.Factory(UserDao, session_factory=db.provided.session)
user_serial = providers.Singleton(UserSerializer)
user_service = providers.Factory(UserService, user_dao=user_dao, user_serial=user_serial)
serializer.py
class UserSerializer:
def serial_user_all_data(self, data):
print("Data: ", data)
return [
{
"user": 1
},
{
"user": 2
},
{
"user": 3
}
]
service.py
from src.api.users.dao import UserDao
from src.api.users.serializer import UserSerializer
class UserService:
def __init__(self, user_dao: UserDao, user_serial: UserSerializer):
self.user_dao = user_dao
self.user_serial = user_serial
def get_all_user(self):
return self.user_serial.serial_user_all_data(self.user_dao.get_user_all())
db
from contextlib import contextmanager, AbstractContextManager
from typing import Callable
import logging
from sqlalchemy import create_engine, orm
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import Session
logger = logging.getLogger(__name__)
Base = declarative_base()
class Database:
def __init__(self, db_url: str) -> None:
print("db_url: ", db_url)
self._engine = create_engine(db_url, echo=True)
self._session_factory = orm.scoped_session(
orm.sessionmaker(
autocommit=False,
autoflush=False,
bind=self._engine,
),
)
def create_database(self) -> None:
Base.metadata.create_all(self._engine)
@contextmanager
def session(self) -> Callable[..., AbstractContextManager[Session]]:
session: Session = self._session_factory()
try:
print("生成 db session ...........")
yield session
except Exception:
logger.exception("Session rollback because of exception")
session.rollback()
raise
finally:
print("关闭 这个 session ................")
session.close()
from src.api.ApplicationModule import Application
def init_di(app):
container = Application()
# db = container.db()
# db.create_database()
container.wire(modules=[__name__])
app.container = container
return app
import uvicorn
from fastapi import FastAPI
from src.init_server.ioc import init_di
from src.api.users.controller import router
def create_app() -> FastAPI:
app = FastAPI()
init_di(app)
app.include_router(router)
return app
app = create_app()
if __name__ == '__main__':
uvicorn.run("main:app", host="0.0.0.0", port=8000)
python main.py 即可
很是优雅 [doge]
这只是一个简单的web应用demo, 具体的使用还需要继续优化.