功能和问题

  • 功能描述:sqlalchemy连接MySQL数据库
  • 问题描述:用于fastapi项目中连接MySQL数据库

代码片段展示

from sqlalchemy import create_engine, Column, Integer, String, Enum, DateTime
from sqlalchemy.orm import sessionmaker, declarative_base
from datetime import datetime
from contextlib import contextmanager

Base = declarative_base()

class User(Base):
    __tablename__ = "user"
    id = Column(Integer, primary_key=True, autoincrement=True)
    username = Column(String(255), nullable=False, unique=True)  # 加唯一约束
    email = Column(String(255), nullable=False, unique=True)  # 加唯一约束
    password = Column(String(255), nullable=False)
    avatar = Column(String(255), nullable=False)
    status = Column(String(20), nullable=False, default='active')
    created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
    updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False)

class DatabaseManager:
    def __init__(self, db_url):
        self.engine = create_engine(db_url)
        self.Session = sessionmaker(bind=self.engine, expire_on_commit=False)

    def create_tables(self):
        """建表(单独的方法,手动调用)"""
        Base.metadata.create_all(self.engine)

    @contextmanager
    def session_scope(self):
        """提供自动关闭的 session 上下文"""
        session = self.Session()
        try:
            yield session
            session.commit()
        except Exception as e:
            session.rollback()
            raise e
        finally:
            session.close()

    def add_user(self, username, email, password, avatar):
        """添加用户(带异常处理)"""
        with self.session_scope() as session:
            new_user = User(
                username=username,
                email=email,
                password=password,
                avatar=avatar
            )
            session.add(new_user)
            # 自动 commit

    def get_user_by_id(self, user_id):
        """根据 ID 查询用户"""
        with self.session_scope() as session:
            return session.query(User).filter(User.id == user_id).first()

    def get_all_users(self):
        """查询所有用户,并在会话内部打印信息"""
        with self.session_scope() as session:
            users = session.query(User).all()
            return users

    def get_users_by_status(self, status):
        """按状态查询用户"""
        with self.session_scope() as session:
            return session.query(User).filter(User.status == status).all()


# 使用示例
if __name__ == "__main__":
    # 初始化
    db_manager = DatabaseManager("mysql+pymysql://root:123456@127.0.0.1/test")

    # 建表(只需执行一次)
    # db_manager.create_tables()

    # 添加用户
    try:
        db_manager.add_user(
            username="李四",
            email="lisi@example.com",  # ✅ 正确的邮箱格式
            password="123456",
            avatar="https://example.com/avatar.png"
        )
        print("用户添加成功")
    except Exception as e:
        print(f"添加失败: {e}")

    # 查询用户
    db_manager.get_all_users()

fastApi集成sqlalchemy

1、环境准备 安装 fastapi sqlalchemy pymysql asyncmy uvicorn pydantic-settings 等必要的库 2、配置文件 config.py

from pydantic_settings import BaseSettings

class DatabaseSettings(BaseSettings):
    """数据库配置设置"""
    
    # 数据库基本信息
    DB_HOST: str = "127.0.0.1"
    DB_PORT: int = 3306
    DB_USER: str = "root"
    DB_PASSWORD: str = "123456"
    DB_NAME: str = "test"
    
    # 连接池配置
    DB_POOL_SIZE: int = 10
    DB_MAX_OVERFLOW: int = 20
    DB_POOL_TIMEOUT: int = 30
    DB_POOL_RECYCLE: int = 3600
    
    # 编码配置
    DB_CHARSET: str = "utf8mb4"
    
    # SQLALCHEMY配置
    SQLALCHEMY_ECHO: bool = False
    SQLALCHEMY_TRACK_MODIFICATIONS: bool = False
    
    @property
    def database_url(self) -> str:
        """生成同步数据库URL"""
        return (
            f"mysql+pymysql://{self.DB_USER}:{self.DB_PASSWORD}@"
            f"{self.DB_HOST}:{self.DB_PORT}/{self.DB_NAME}"
            f"?charset={self.DB_CHARSET}"
        )
    
    @property
    def async_database_url(self) -> str:
        """生成异步数据库URL"""
        return (
            f"mysql+aiomysql://{self.DB_USER}:{self.DB_PASSWORD}@"
            f"{self.DB_HOST}:{self.DB_PORT}/{self.DB_NAME}"
            f"?charset={self.DB_CHARSET}"
        )
    
    class Config:
        env_file = ".env"
        env_file_encoding = "utf-8"
        case_sensitive = True


# 全局配置实例
settings = DatabaseSettings()

3、数据库连接管理 创建connection.py, 定义DatabaseManager,用于管理数据库连接和会话,创建 init_sync_engine 和 init_async_engine 两种初始化引擎 sessionmaker 被用来创建同步和异步的会话工厂

from sqlalchemy import create_engine
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from sqlalchemy.orm import sessionmaker, declarative_base
from sqlalchemy.pool import QueuePool
from contextlib import contextmanager
from typing import Generator, AsyncGenerator
import logging

from .config import settings

# 配置日志
logger = logging.getLogger(__name__)

# 创建基类
Base = declarative_base()

class DatabaseManager:
    """数据库管理器 - 生产级别封装"""
    
    def __init__(self):
        self._sync_engine = None
        self._async_engine = None
        self._sync_session_local = None
        self._async_session_local = None
        
    def init_sync_engine(self):
        """初始化同步数据库引擎"""
        if self._sync_engine is None:
            self._sync_engine = create_engine(
                settings.database_url,
                poolclass=QueuePool,
                pool_size=settings.DB_POOL_SIZE,
                max_overflow=settings.DB_MAX_OVERFLOW,
                pool_timeout=settings.DB_POOL_TIMEOUT,
                pool_recycle=settings.DB_POOL_RECYCLE,
                echo=settings.SQLALCHEMY_ECHO,
                pool_pre_ping=True,  # 连接前检测
            )
            logger.info("同步数据库引擎初始化完成")
        
        if self._sync_session_local is None:
            self._sync_session_local = sessionmaker(
                bind=self._sync_engine,
                autocommit=False,
                autoflush=False,
                expire_on_commit=False
            )
            
    def init_async_engine(self):
        """初始化异步数据库引擎"""
        if self._async_engine is None:
            self._async_engine = create_async_engine(
                settings.async_database_url,
                pool_size=settings.DB_POOL_SIZE,
                max_overflow=settings.DB_MAX_OVERFLOW,
                pool_timeout=settings.DB_POOL_TIMEOUT,
                pool_recycle=settings.DB_POOL_RECYCLE,
                echo=settings.SQLALCHEMY_ECHO,
                pool_pre_ping=True,
            )
            logger.info("异步数据库引擎初始化完成")
            
        if self._async_session_local is None:
            self._async_session_local = sessionmaker(
                bind=self._async_engine,
                class_=AsyncSession,
                autocommit=False,
                autoflush=False,
                expire_on_commit=False
            )
    
    @property
    def sync_engine(self):
        """获取同步引擎"""
        if self._sync_engine is None:
            self.init_sync_engine()
        return self._sync_engine
    
    @property
    def async_engine(self):
        """获取异步引擎"""
        if self._async_engine is None:
            self.init_async_engine()
        return self._async_engine
    
    @property
    def sync_session_local(self):
        """获取同步会话工厂"""
        if self._sync_session_local is None:
            self.init_sync_engine()
        return self._sync_session_local
    
    @property
    def async_session_local(self):
        """获取异步会话工厂"""
        if self._async_session_local is None:
            self.init_async_engine()
        return self._async_session_local
    
    @contextmanager
    def get_sync_db(self) -> Generator:
        """获取同步数据库会话上下文管理器"""
        db = self.sync_session_local()
        try:
            yield db
            db.commit()
        except Exception as e:
            db.rollback()
            logger.error(f"数据库操作失败: {e}")
            raise
        finally:
            db.close()
    
    async def get_async_db(self) -> AsyncGenerator[AsyncSession, None]:
        """获取异步数据库会话"""
        async with self.async_session_local() as session:
            try:
                yield session
                await session.commit()
            except Exception as e:
                await session.rollback()
                logger.error(f"异步数据库操作失败: {e}")
                raise
    
    def create_tables(self):
        """创建所有表"""
        try:
            Base.metadata.create_all(bind=self.sync_engine)
            logger.info("数据库表创建完成")
        except Exception as e:
            logger.error(f"创建数据库表失败: {e}")
            raise
    
    def drop_tables(self):
        """删除所有表"""
        try:
            Base.metadata.drop_all(bind=self.sync_engine)
            logger.info("数据库表删除成功")
        except Exception as e:
            logger.error(f"删除数据库表失败: {e}")
            raise
    
    async def close_connections(self):
        """关闭所有数据库连接"""
        if self._async_engine:
            await self._async_engine.dispose()
        if self._sync_engine:
            self._sync_engine.dispose()
        logger.info("数据库连接已关闭")


# 全局数据库管理器实例
db_manager = DatabaseManager()

4、在fastapi中调用
在启动的时候调用数据库初始化,

# main.py

# Lifespan 事件处理器
@asynccontextmanager
async def lifespan(app: FastAPI):
    """应用生命周期管理"""
    try:
        # 应用启动时初始化数据库连接
        db_manager.init_sync_engine()
        db_manager.init_async_engine()
        db_manager.create_tables()
        logger.info("数据库连接初始化成功")
        yield  # 应用运行期间保持连接
    finally:
        # 应用关闭时清理资源
        await db_manager.close_connections()
        logger.info("数据库连接已关闭")

# 创建FastAPI应用
app = FastAPI(
    title="FastAPI Production Project",
    description="生产级别的FastAPI项目,包含完整的数据库集成",
    version="1.0.0",
    lifespan=lifespan
)

from fastapi import Depends, HTTPException, status
from sqlalchemy.orm import Session
from sqlalchemy.ext.asyncio import AsyncSession
from typing import Generator, AsyncGenerator
import logging

from .connection import db_manager

logger = logging.getLogger(__name__)


def get_sync_db() -> Generator[Session, None, None]:
    """
    同步数据库会话依赖
    
    使用示例:
    @app.get("/users/")
    def get_users(db: Session = Depends(get_sync_db)):
        users = db.query(User).all()
        return users
    """
    with db_manager.get_sync_db() as db:
        try:
            yield db
        except Exception as e:
            logger.error(f"同步数据库会话错误: {e}")
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail="数据库操作失败"
            )


async def get_async_db() -> AsyncGenerator[AsyncSession, None]:
    """
    异步数据库会话依赖
    
    使用示例:
    @app.get("/users/")
    async def get_users(db: AsyncSession = Depends(get_async_db)):
        result = await db.execute(select(User))
        users = result.scalars().all()
        return users
    """
    async with db_manager.get_async_db() as db:
        try:
            yield db
        except Exception as e:
            logger.error(f"异步数据库会话错误: {e}")
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail="数据库操作失败"
            )


class DatabaseDependencies:
    """数据库依赖集合类"""
    
    @staticmethod
    def sync_db():
        """同步数据库依赖"""
        return Depends(get_sync_db)
    
    @staticmethod
    def async_db():
        """异步数据库依赖"""
        return Depends(get_async_db)


# 便捷别名
SyncDB = DatabaseDependencies.sync_db()
AsyncDB = DatabaseDependencies.async_db()

坑点bcrypt和passlib的版本要对应: “bcrypt==3.2.0”, “passlib==1.7.4”,

总结

app/ ├── database/ │ ├── config.py # 数据库配置 │ ├── connection.py # 数据库连接管理 │ └── dependencies.py # 依赖注入 ├── models/ # SQLAlchemy 模型 ├── schemas/ # Pydantic Schema ├── crud/ # 数据库操作逻辑 └── main.py # FastAPI 应用入口