[ fastapi使用sqlalchemy2.0异步数据库 ]


fastapi使用sqlalchemy2.0异步数据库

pip install SQLAlchemy
pip install aiomysql
pip install pydantic-settings
pip install python-dotenv

配置文件config.py

from pydantic_settings import BaseSettings
from functools import lru_cache

class Settings(BaseSettings):
    app_name: str = "chf-app"
    database_url: str = 'mysql+aiomysql://root:chenhaifeng@47.120.55.99:3306/fastapi'
    secret: str = 'SECRET'

    class Config:
        env_file = ".env"


@lru_cache
def get_settings():
    return Settings()

初始化数据库连接mysql.py

from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine
from sqlalchemy.ext.asyncio import AsyncSession
from typing import AsyncGenerator
from config.config import get_settings
import databases
from .model import Base

DATABASE_URL = get_settings().database_url
database = databases.Database(DATABASE_URL, min_size=2, max_size=5)
engine = create_async_engine(DATABASE_URL, echo=True)  # echo开启sql打印
async_session_maker = async_sessionmaker(engine, expire_on_commit=False)


class DALGetter:
    def __init__(self, dal_cls):
        self.dal_cls = dal_cls

    async def __call__(self):
        async with async_session_maker() as session:
            async with session.begin():
                yield self.dal_cls(session)


# 获取数据库连接
async def get_async_session() -> AsyncGenerator[AsyncSession, None]:
    async with async_session_maker() as session:
        yield session


# 创建表
async def create_db_and_tables():
    from application.ucenter.model import User, UserRole, Role, RolePower, Power
    async with engine.begin() as conn:
        if get_settings().drop_all_table:
            await conn.run_sync(Base.metadata.drop_all)
        if get_settings().create_all_table:
            await conn.run_sync(Base.metadata.create_all)

基础模型类model.py

from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.ext.asyncio import AsyncAttrs

class Base(AsyncAttrs, DeclarativeBase):
    pass

使用views.py

from fastapi import APIRouter

from sqlalchemy import select
from sqlalchemy.orm import selectinload
from extensions.db.mysql import get_async_session
from application.ucenter.model import Role, Power
from .schema import GetRoleDetail, AddRole, UpdateRole, AddPowerReq, AddPowerResp, RemodePowerReq, RemodePowerResp
from sqlalchemy.ext.asyncio import AsyncSession
from fastapi import Depends

role_router = APIRouter()


@role_router.get("/detail", response_model=GetRoleDetail)
async def get_role(pk: int, session: AsyncSession = Depends(get_async_session)):
    """
    获取单个角色
    """
    query = select(Role).where(Role.id == pk)
    result = await session.execute(query)
    return result.unique().scalar_one_or_none()


@role_router.post("/add")
async def add_role(data: AddRole, session: AsyncSession = Depends(get_async_session)):
    role = Role(**data.model_dump())
    session.add(role)
    await session.commit()
    await session.refresh(role)
    return {"msg": "ok", "id": role.id}


@role_router.put("/update")
async def update_role(pk: int, data: UpdateRole, session: AsyncSession = Depends(get_async_session)):
    query = select(Role).where(Role.id == pk)
    res = await session.execute(query)
    role = res.unique().scalar_one_or_none()
    for k, v in data.model_dump().items():
        setattr(role, k, v)
    session.add(role)
    await session.commit()
    await session.refresh(role)
    return {"msg": "ok", "id": role.id}


@role_router.delete("/delete")
async def delete_role(pk: int, session: AsyncSession = Depends(get_async_session)):
    query = select(Role).where(Role.id == pk)
    res = await session.execute(query)
    role = res.unique().scalar_one_or_none()
    session.delete(role)
    await session.commit()
    return {"msg": "ok", "id": pk}


@role_router.post("/add/powers", response_model=AddPowerResp)
async def add_powers(req: AddPowerReq, session: AsyncSession = Depends(get_async_session)):
    """
        给角色(role) 添加权限(power)
    """
    # 查询power是否存在
    power_query = select(Power).where(Power.id.in_(req.power_list))
    power_resp = await session.execute(power_query)

    # 查询role是否存在,且预加载多对多powers字段(添加时才能触发,自动查重复添加)
    role_resp = await session.scalars(select(Role).where(Role.id == req.role_id).options(selectinload(Role.powers)))
    role = role_resp.first()
    role.powers = power_resp.scalars().all()
    session.add(role)
    await session.commit()

    return AddPowerResp(id=1, msg="ok")


@role_router.post("/remode/powers", response_model=RemodePowerResp)
async def remode_powers(req: RemodePowerReq, session: AsyncSession = Depends(get_async_session)):
    """
        角色(role) 移除权限(power)
    """
    power_query = select(Power).where(Power.id.in_(req.power_list))
    power_resp = await session.execute(power_query)

    role_resp = await session.scalars(select(Role).where(Role.id == req.role_id).options(selectinload(Role.powers)))
    role = role_resp.first()
    for power in power_resp.scalars():
        role.powers.remove(power)
    await session.commit()
    return RemodePowerResp(msg="ok")


@role_router.post("/test")
async def test_role(session: AsyncSession = Depends(get_async_session)):
    async with session.begin():
        session.add_all(
            [
                Role(name="角色1"),
                Role(name="角色2"),
                Role(name="角色3"),
            ]
        )
    await session.commit()
    return {"msg": "ok"}