【FastAPI】使用 FastAPI 和 SQLAlchemy 记录数据库操作日志:基于装饰器的实现

在后端开发中,记录用户对数据库的操作日志(如增、删、改)不仅是调试和监控的有效工具,还能提升系统的可维护性和安全性。本文将介绍如何在 FastAPI 中使用装饰器来记录数据库操作日志,并保留修改前和修改后的内容。

一、为什么需要操作日志?

  1. 审计:操作日志能够追踪谁在何时对数据进行了哪些修改,尤其在处理敏感数据时非常有必要。
  2. 调试:当系统出现问题时,通过日志可以快速查明问题根源,尤其是在涉及数据一致性的问题时。
  3. 安全:可以识别恶意操作或非授权修改,从而采取相应的安全措施。

通过记录操作前后的数据状态,我们能够清楚地知道数据库的变化,并且可以在系统出错或需要回滚时进行详细分析。

二、项目设置

1. 数据库设置

首先,我们创建一个日志记录表,用于保存用户操作的详细信息。我们将使用 PostgreSQL 来存储数据(当然,你可以使用任何你熟悉的数据库系统)。

CREATE TABLE operation_logs (
    id SERIAL PRIMARY KEY,
    user_id INTEGER NOT NULL, -- 操作用户的ID
    operation_type VARCHAR(50) NOT NULL, -- 操作类型,INSERT, UPDATE, DELETE
    table_name VARCHAR(100) NOT NULL, -- 操作的表名
    record_id INTEGER NOT NULL, -- 被操作记录的ID
    operation_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- 操作时间
    before_change TEXT, -- 修改前的数据
    after_change TEXT -- 修改后的数据
);

2. 使用 SQLAlchemy 进行数据库操作

为了更好地与数据库交互,我们使用 SQLAlchemy 作为 ORM。这里是基本的 FastAPI 与 SQLAlchemy 的配置:

from fastapi import FastAPI, Depends
from sqlalchemy import create_engine, Table, MetaData
from sqlalchemy.orm import sessionmaker

app = FastAPI()

# 数据库连接配置
DATABASE_URL = "postgresql://user:password@localhost/dbname"
engine = create_engine(DATABASE_URL)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

# 获取数据库会话
def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

3. 编写日志记录装饰器

装饰器是一种非常简洁且优雅的方式,可以将日志记录与业务逻辑分离。接下来,我们将创建一个装饰器,专门用于记录数据库增删改操作时的日志信息。

from functools import wraps
from datetime import datetime
import json

def log_changes(operation_type):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            db = kwargs.get('db')  # 获取数据库会话
            user_id = kwargs.get('user_id')  # 获取用户ID
            table_name = kwargs.get('table_name')  # 操作的表名
            record_id = kwargs.get('record_id')  # 操作记录的ID
            
            # 获取修改前的数据
            before_change = func.__name__ == "delete_item" and "N/A" or db.query(Table(table_name)).filter_by(id=record_id).first()
            before_change_data = json.dumps(dict(before_change)) if before_change else "N/A"

            result = func(*args, **kwargs)  # 执行实际的增删改操作

            # 获取修改后的数据
            after_change = func.__name__ == "delete_item" and "N/A" or db.query(Table(table_name)).filter_by(id=record_id).first()
            after_change_data = json.dumps(dict(after_change)) if after_change else "N/A"
            
            # 记录操作日志
            log_operation(db, user_id, operation_type, table_name, record_id, before_change_data, after_change_data)

            return result
        return wrapper
    return decorator

4. 日志记录函数

我们还需要编写一个 log_operation 函数,用来将日志信息插入到数据库中。

def log_operation(db, user_id, operation_type, table_name, record_id, before_change, after_change):
    query = f"""
    INSERT INTO operation_logs (user_id, operation_type, table_name, record_id, operation_time, before_change, after_change)
    VALUES ({user_id}, '{operation_type}', '{table_name}', {record_id}, '{datetime.now()}', '{before_change}', '{after_change}');
    """
    db.execute(query)
    db.commit()

5. 应用装饰器

现在我们可以将装饰器应用到 FastAPI 的路由中,以便在用户执行增删改操作时自动记录操作日志。

@app.post("/items/")
@log_changes("INSERT")
def create_item(item: dict, user_id: int, db: SessionLocal = Depends(get_db)):
    # 执行插入操作
    table_name = "items"
    record_id = 123  # 插入记录后生成的ID

    # 实际插入代码 (示例)
    db.execute(f"INSERT INTO {table_name} (name, value) VALUES ('{item['name']}', '{item['value']}')")
    db.commit()

    return {"status": "success", "record_id": record_id}

@app.put("/items/{item_id}")
@log_changes("UPDATE")
def update_item(item_id: int, updated_item: dict, user_id: int, db: SessionLocal = Depends(get_db)):
    # 执行更新操作
    table_name = "items"
    
    # 实际更新代码 (示例)
    db.execute(f"UPDATE {table_name} SET name = '{updated_item['name']}', value = '{updated_item['value']}' WHERE id = {item_id}")
    db.commit()

    return {"status": "updated", "item_id": item_id}

@app.delete("/items/{item_id}")
@log_changes("DELETE")
def delete_item(item_id: int, user_id: int, db: SessionLocal = Depends(get_db)):
    # 执行删除操作
    table_name = "items"
    
    # 实际删除代码 (示例)
    db.execute(f"DELETE FROM {table_name} WHERE id = {item_id}")
    db.commit()

    return {"status": "deleted", "item_id": item_id}

三、总结

通过本文的示例,我们展示了如何在 FastAPI 中结合 SQLAlchemy 使用装饰器来记录数据库的增删改操作日志。这种方法的优势在于它与业务逻辑分离,使用简单且易于扩展。此外,日志表中保存了修改前后的数据,使得系统在调试和审计时更加高效和透明。

希望这篇文章能帮助你在项目中实现类似的功能!如果你有任何问题或建议,欢迎在评论区留言。

### 集成使用数据库FastAPI 项目中集成并操作数据库涉及多个方面,包括安装必要的客户端、配置连接池以及编写具体的 CRUD 操作逻辑。 #### 安装 MySQL Client 为了能够顺利地与 MySQL 数据库交互,在项目的依赖环境中需先安装 `mysqlclient` 库。这一步骤对于确保后续可以正常建立到 MySQL 的连接至关重要[^1]。 ```bash pip install mysqlclient ``` #### 创建异步数据库引擎 当考虑性能优化时,推荐采用 SQLAlchemy 来构建异步模式下的数据库引擎实例。这样做不仅有助于提高并发处理能力,还能简化事务管理错误恢复机制的设计[^2]。 ```python from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession from sqlalchemy.orm import sessionmaker DATABASE_URL = "mysql+aiomysql://user:password@localhost/dbname" engine = create_async_engine(DATABASE_URL, echo=True) AsyncSessionFactory = sessionmaker( engine, class_=AsyncSession, expire_on_commit=False ) ``` #### 连接 MySQL 数据库的具体实践 以一个典型的 FastAPI 工程结构为例 (`run.py`, `database.py`, `main.py`, `model.py`, `schemas.py`, `crud.py`) ,其中 `database.py` 负责定义所有关于数据库会话管理的部分;而实际的数据访问对象 (DAOs) 则放置于 `crud.py` 中以便分离关注点[^3]。 ```python # database.py from sqlalchemy.ext.declarative import declarative_base Base = declarative_base() async def get_db(): async with AsyncSessionFactory() as session: yield session ``` ```python # crud.py 示例片段 from sqlalchemy.future import select from .models import User async def read_users(db_session): result = await db_session.execute(select(User)) return result.scalars().all() ``` 通过上述方法论指导下的架构设计,开发者可以在 FastAPI 应用程序里高效且安全地执行各种类型的数据库查询服务端业务流程。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

写bug如流水

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值