Sqladmin - FastAPI框架下一键生成管理后台

发布于:2025-03-24 ⋅ 阅读:(25) ⋅ 点赞:(0)

介绍

Github 开源地址:

https://github.com/aminalaee/sqladmin

网站说明链接地址:

https://aminalaee.dev/sqladmin/

一个现代、优雅的 SQLAlchemy 管理后台工具,非常适合用在 FastAPI 项目中

SQLAdmin 是一个基于 FastAPI + SQLAlchemy 构建的管理后台框架,灵感来自于 Django Admin,目标是为 Python 项目提供:

• 🚀 简洁易用的后台界面

• 📋 基于 SQLAlchemy 的模型自动管理

• ⚙️ 快速 CRUD(增删改查)

• ✨ Jinja2 渲染漂亮的 Bootstrap 风格后台

功能 描述
🎨 UI 基于 Tabler(Bootstrap 风格)构建
🔁 CRUD 自动化 自动根据模型生成增删改查
🔍 搜索 / 过滤 表字段搜索、过滤支持
🧩 多数据库支持 支持 SQLite、PostgreSQL、MySQL 等
🔐 自定义认证 可扩展自定义登录(如你用的 AdminAuth)
🧠 异步支持 与 FastAPI 完美配合,支持异步视图
🔧 高度可配置 支持列隐藏、格式化、标签等配置项

支持models 的类型:

✅ 支持 SQLAlchemy 吗? 是,必须基于 SQLAlchemy ORM 模型
✅ 支持 SQLAlchemy 1.x 吗?
✅ 支持 SQLAlchemy 2.x 吗? 是,从 sqladmin 0.15.0+ 开始全面支持
❌ 支持 Tortoise ORM 吗? 不支持
❌ 支持 Django ORM 吗? 不支持

为什么使用?

很多项目都是异步,而异步里面ORM 性能最好的就是 SQLAlchemy 2.0

特性 优势 是否推荐
✅ 新的 2.0 风格语法 更清晰、类型安全,统一 async / sync 用法 强烈推荐
✅ 原生异步支持(async_engine) 适配 FastAPI、aiohttp 等异步框架
✅ Declarative Mapping 更加清晰 不再混用 Base = declarative_base() 和 mapper()
✅ 更强的类型提示支持 IDE 友好,调试更舒服
✅ Session 语义改进 显式事务控制、更加安全
✅ 官方长期维护方向 所有新功能只在 2.x 上更新
特性 / 框架 SQLAlchemy 2.0 (async) Tortoise ORM asyncpg (原生驱动)
⭐ 异步支持 ✅ 原生 async API(2.0 起) ✅ 原生 async ✅ 原生 async
🎯 ORM 支持 ✅ 完整 ORM(经典 + 声明式) ✅ 类 Django 的 ORM ❌ 没有 ORM,需手写 SQL
⚙️ 事务处理 ✅ async with session.begin() ✅ transaction() 上下文 ✅ 需手动写 SQL + 事务处理
🔧 灵活性 ✅ 高(支持 ORM + Core) 中等(ORM风格较固定) 极高(最贴近 SQL)
📚 学习曲线 稍高(但文档全) 较低,适合 Django 用户 中等(需掌握 SQL)
🚀 性能 优(合理用 async) 优(轻量) 最优(直接操作 protocol)
✅ 类型支持 ✅ Pydantic 配合良好 ✅ 也能配合 Pydantic 使用 ❌ 无自动转换,手动处理
🧩 支持的数据库 多(PostgreSQL, MySQL, SQLite等) PostgreSQL、MySQL、SQLite PostgreSQL 专用
🔍 社区 & 文档 非常成熟(官方长期维护) 活跃,偏小众 官方是 PostgreSQL 的推荐驱动
你的需求 推荐方案
需要强大的 ORM + 类型检查 + 可扩展性 SQLAlchemy 2.0 + Async
想快速上手、有 Django ORM 背景 Tortoise ORM
要极致性能,手写 SQL 不怕麻烦 asyncpg

结合应用

from sqlalchemy import Column, Integer, String, create_engine,Enum as SAEnum,JSON
from sqladmin.authentication import AuthenticationBackend
import json
import hashlib
from sqlalchemy import event

from sqlalchemy.orm import declarative_base
from enum import Enum
from sqlalchemy.ext.hybrid import hybrid_property

Base = declarative_base()
engine = create_engine(
    "sqlite:///example.db",
    connect_args={"check_same_thread": False},
)

# 定义枚举类型
class GenderEnum(str, Enum):
    MALE = "male"
    FEMALE = "female"
    OTHER = "other"

class User(Base):
    __tablename__ = "users"

    id = Column(Integer, primary_key=True)
    name = Column(String)
    gender = Column(SAEnum(GenderEnum), nullable=False, comment="性别")
    password = Column("password", String, nullable=False)  # 真实存储加密后的密码

class JsonConfig(Base):
    __tablename__ = "json_config"

    id = Column(Integer, primary_key=True)
    name = Column(String)
    config = Column(JSON)

Base.metadata.create_all(engine)  # Create tables,后面改了字段需要删除

from fastapi import FastAPI
from sqladmin import Admin, ModelView
from fastapi import Request

app = FastAPI()

class AdminAuth(AuthenticationBackend):
    async def login(self, request: Request) -> bool:
        form = await request.form()
        username, password = form["username"], form["password"]

        # Validate username/password credentials
        # And update session
        if username == "admin" and password == "123456":
            request.session.update({"sqladmin_token": "..."})
            return True
        else:
            return False
        return True

    async def logout(self, request: Request) -> bool:
        # Usually you'd want to just clear the session
        request.session.clear()
        return True

    async def authenticate(self, request: Request) -> bool:
        token = request.session.get("sqladmin_token")

        if not token:
            return False

        # Check the token in depth
        return True

authentication_backend = AdminAuth(secret_key="secret_key")

admin = Admin(app, engine,base_url="/super",authentication_backend=authentication_backend)

class UserAdmin(ModelView, model=User):
    self_md5_salt = "123456"
    column_list = [User.id, User.name,User.gender,User.password]

    # 自定义列标签(让 SQLAdmin UI 显示中文)
    column_labels = {
        "id": "用户 ID",
        "name": "姓名",
        "gender": "性别",
        "password": "密码",
    }
    async def insert_model(self, request: Request, data: dict):
        """拦截插入逻辑,确保 `password` 经过 MD5 加密"""
        raw_password = data.pop("password", None)
        if raw_password:
            data["password"] = hashlib.md5((raw_password+self.self_md5_salt).encode()).hexdigest()
        return await super().insert_model(request, data)

    async def update_model(self, request: Request, pk: str, data: dict):
        """拦截更新逻辑,确保 `password` 经过 MD5 加密"""
        raw_password = data.pop("password", None)
        if raw_password:
            data["password"] = hashlib.md5((raw_password+self.self_md5_salt).encode()).hexdigest()
        return await super().update_model(request, pk, data)

class JsonConfigAdmin(ModelView, model=JsonConfig): 
    column_list = [JsonConfig.id, JsonConfig.name,JsonConfig.config]
    # 前端页面插入的时候 只能使用双引号的json 不能使用单引号的json

admin.add_view(UserAdmin)
admin.add_view(JsonConfigAdmin)

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="127.0.0.1", port=8000)
# uvicorn test_sqladmin:app --host 127.0.0.1 --port 8000 --reload
# 后台地址: http://127.0.0.1:8000/super

# pip install itsdangerous  sqladmin 

网站公告

今日签到

点亮在社区的每一天
去签到