SQLAdmin 使用指南

1,230 阅读6分钟

1. 简介

SQLAdmin 是一个用于 Starlette/FastAPI 应用的管理界面生成库。它提供了一个简单而强大的方式来为你的数据库模型创建CRUD(创建、读取、更新、删除)管理界面。SQLAdmin 支持多种数据库后端,包括 SQLAlchemy、SQLModel、Tortoise ORM 等。

本文档将详细介绍如何在项目中使用 SQLAdmin。


2. 基础配置

2.1 初始化配置

main.py 中,我们需要进行以下基础配置:

from sqladmin import Admin
from sqlalchemy import create_engine

# 创建数据库引擎
engine = create_engine(
    "your_database_url",
    pool_pre_ping=True,
    echo=True  # 启用SQL语句日志
)

# 创建Admin实例
admin = Admin(
    app,                # FastAPI 应用实例
    engine,            # SQLAlchemy 引擎
    base_url="/admin", # 管理界面的基础URL
    title="管理后台",   # 管理界面标题
)

2.2 认证配置

SQLAdmin 提供了灵活的认证机制,通过继承 AuthenticationBackend 类来实现自定义认证。以下是一个完整的认证配置示例:

from sqladmin.authentication import AuthenticationBackend
from starlette.requests import Request
import logging

# 设置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

class AdminAuth(AuthenticationBackend):
    async def login(self, request: Request) -> bool:
        """
        处理登录请求
        - 从表单中获取用户名和密码
        - 验证凭据
        - 设置会话状态
        """
        try:
            form = await request.form()
            username, password = form["username"], form["password"]
            
            # 验证逻辑(示例)
            # 实际应用中应该:
            # 1. 从数据库验证用户
            # 2. 使用安全的密码哈希
            # 3. 实现更复杂的安全措施
            if username == "admin" and password == "admin123":
                # 设置会话状态
                request.session["admin_auth"] = True
                request.session["username"] = username
                return True
            
            # 登录失败处理
            request.session["login_message"] = "用户名或密码错误"
            return False
            
        except Exception as e:
            logger.error(f"Login error: {str(e)}")
            request.session["login_message"] = "登录时发生错误"
            return False

    async def logout(self, request: Request) -> bool:
        """
        处理登出请求
        - 清除会话
        - 记录日志
        """
        try:
            request.session.clear()
            return True
        except Exception as e:
            logger.error(f"Logout error: {str(e)}")
            return False

    async def authenticate(self, request: Request) -> bool:
        """
        验证用户是否已认证
        - 检查会话状态
        - 记录调试日志
        """
        try:
            return request.session.get("admin_auth", False)
        except Exception as e:
            logger.error(f"Authentication error: {str(e)}")
            return False

# 在 FastAPI 应用中配置认证
from starlette.middleware.sessions import SessionMiddleware

# 1. 添加会话中间件
app.add_middleware(
    SessionMiddleware,
    secret_key="your-secret-key-here",  # 使用安全的密钥
    session_cookie="admin-session",      # 自定义会话 cookie 名称
    max_age=None                         # 会话过期时间,None 表示浏览器关闭时过期
)

# 2. 配置 Admin 认证
admin = Admin(
    app,
    engine,
    authentication_backend=AdminAuth(secret_key="your-admin-secret-key"),
    base_url="/admin",
    title="管理后台"
)

认证配置的关键点:

  1. 会话管理
  • 使用 SessionMiddleware 管理用户会话
  • 配置安全的密钥和会话选项
  • 可以自定义会话 cookie 的名称和过期时间
  1. 认证类实现
  • login(): 处理登录请求,验证用户凭据
  • logout(): 处理登出请求,清除会话
  • authenticate(): 验证用户是否已认证
  1. 安全建议
  • 使用环境变量存储密钥
  • 实现数据库用户验证
  • 使用安全的密码哈希算法
  • 添加登录失败次数限制
  • 实现密码策略
  • 记录详细的安全日志
  1. 错误处理
  • 捕获并记录所有可能的异常
  • 提供用户友好的错误消息
  • 维护详细的日志记录

3. 创建管理视图

3.1 基础视图类

views/base.py 中,我们定义了一个基础视图类:

from sqladmin import ModelView

class BaseModelView(ModelView):
    # 自定义模板
    list_template = "admin/list.html"
    create_template = "admin/create.html"
    edit_template = "admin/edit.html"
    details_template = "admin/details.html"
    
    # 默认配置
    can_create = True    # 允许创建
    can_edit = True      # 允许编辑
    can_delete = True    # 允许删除
    can_view_details = True  # 允许查看详情
    can_export = True    # 允许导出
    page_size = 20       # 每页显示记录数
    column_default_sort = ("id", True)  # 默认排序,按照id倒序排序

3.2 创建模型视图

对于每个数据库模型,我们可以创建对应的管理视图:

class UserAdmin(BaseModelView, model=User):
    # 视图名称
    name = "用户"
    name_plural = "用户管理"
    
    # 分类
    category = "用户管理"
    
    # 显示的列
    column_list = ["id", "username", "email", "created_at"]
    
    # 列标签映射
    column_labels = {
        "id": "ID",
        "username": "用户名",
        "email": "邮箱",
        "created_at": "创建时间"
    }

4. 高级功能

4.1 自定义列表显示

可以通过重写 get_list_value 方法来自定义列表页面的值显示。这个方法接收两个参数:

  • model: 当前数据行的模型实例

  • name: 当前列的名称

    def get_list_value(self, model, name): """ 自定义列表显示值

    参数:
        model: SQLAlchemy 模型实例,比如 User 对象
        name: 列名,比如 'status', 'username' 等
    
    示例:
        如果 model 是 User 实例,name 是 'status'
        则 getattr(model, name) 相当于 model.status
    """
    # getattr(object, attribute) 等同于 object.attribute
    value = getattr(model, name)
    
    # 根据不同的列名进行不同的处理
    if name == 'status':
        return '启用' if value else '禁用'
    elif name == 'type':
        # 处理类型枚举
        type_map = {
            1: '个人用户',
            2: '企业用户',
            3: 'VIP用户'
        }
        return type_map.get(value, '未知类型')
    elif name == 'created_at':
        # 格式化日期时间
        return value.strftime('%Y-%m-%d %H:%M:%S') if value else ''
    elif name == 'amount':
        # 格式化金额
        return f'¥{value:.2f}' if value else '¥0.00'
    
    # 如果没有特殊处理,返回原始值
    return value
    

    实际应用示例

    class UserAdmin(BaseModelView, model=User): name = "用户" name_plural = "用户管理"

    def get_list_value(self, model, name):
        value = getattr(model, name)
        
        # 处理用户状态显示
        if name == 'status':
            status_map = {
                0: '<span class="badge bg-danger">禁用</span>',
                1: '<span class="badge bg-success">启用</span>'
            }
            return status_map.get(value, '')
            
        # 处理用户类型显示
        elif name == 'user_type':
            type_map = {
                'personal': '个人',
                'business': '企业',
                'admin': '管理员'
            }
            return type_map.get(value, value)
            
        return value
    

这个方法的常见用途包括:

  1. 格式化显示
  • 日期时间格式化
  • 数字金额格式化
  • 状态文本转换
  1. 值映射转换
  • 将数字状态转为文本
  • 将代码转为可读文本
  • 处理枚举值显示
  1. HTML 渲染
  • 添加样式标签
  • 显示图标或徽章
  • 创建链接
  1. 数据处理
  • 数据脱敏(如手机号、邮箱)
  • 数据截断(长文本)
  • 条件格式化

注意事项:

  • 返回的 HTML 内容需要使用 Markup 类包装,以避免被转义
  • 处理空值和异常情况
  • 考虑性能影响,避免在此方法中进行复杂的数据库查询

4.2 表单字段定制

可以使用 form_overridesform_widget_args 来定制表单字段:

class ArticleAdmin(BaseModelView, model=Article):
    form_overrides = {
        'content': TextAreaField
    }
    form_widget_args = {
        'content': {
            'rows': 10
        }
    }

4.3 分组管理

可以通过 category 属性对管理视图进行分组:

GROUP_NAMES = {
    "user": "用户管理",
    "article": "文章管理",
    "system": "系统管理"
}

class UserAdmin(BaseModelView, model=User):
    category = GROUP_NAMES["user"]

5. 注册视图

main.py 中注册所有管理视图:

# 注册单个视图
admin.add_view(UserAdmin)

# 批量注册视图
for view_class in [
    UserAdmin,
    ArticleAdmin,
    CategoryAdmin
]:
    admin.add_view(view_class)

6. 最佳实践

  1. 视图组织
  • 将相关的视图放在同一个分组下
  • 使用清晰的命名和标签
  • 合理设置每个视图的权限
  1. 安全性
  • 始终启用认证
  • 使用环境变量存储敏感信息
  • 根据需要限制视图权限
  1. 性能优化
  • 合理设置每页显示记录数
  • 只显示必要的列
  • 使用适当的过滤器和排序
  1. 用户体验
  • 提供清晰的列标签
  • 添加必要的帮助文本
  • 自定义复杂数据的显示方式

7. 常见问题

  1. 如何自定义列表显示?
  • 使用 get_list_value 方法
  • 使用 column_formatters 定义格式化函数
  1. 如何添加自定义操作?
  • 继承 BaseModelView 并添加自定义方法
  • 使用 @expose() 装饰器定义新的路由
  1. 如何处理关联数据?
  • 使用 form_ajax_refs 实现异步加载
  • 使用 inline_models 实现内联编辑

8. 总结

SQLAdmin 是一个功能强大的管理界面工具,通过合理的配置和使用,可以快速构建出易用的后台管理系统。在使用过程中,要注意:

  • 遵循最佳实践
  • 注意安全性
  • 关注性能
  • 重视用户体验

希望本文档能帮助你更好地使用 SQLAdmin!