在 Peewee 中基于 MySQL 实现“动态表名”——从连接到查询的完整实战

标签:Python、ORM、Peewee、MySQL、动态表名、分表、实战


一、前言

在使用 Peewee 操作 MySQL 时,有时我们会遇到这样的需求:

  • 数据量太大,需要按年份、月份、用户 ID 等进行分表;
  • 表名在运行时才确定,无法提前写死在模型类中;
  • 但又想继续使用 Peewee 的 ORM 接口进行增删查改。

问题来了:Peewee 的 Meta.table_name 是类级属性,运行时无法修改
解决方案动态创建模型类,每个表名对应一个新类,完全复用 Peewee 的 ORM 能力。


二、环境准备

1. 安装依赖

pip install peewee pymysql

2. 创建数据库(以 MySQL 为例)

CREATE DATABASE IF NOT EXISTS demo DEFAULT CHARSET utf8mb4;

三、连接 MySQL 并封装基础模型

import pymysql
from peewee import *

# 让 pymysql 兼容 MySQLdb
pymysql.install_as_MSQLdb()

# 创建数据库连接
db = MySQLDatabase(
    'demo',
    user='root',
    password='123456',
    host='127.0.0.1',
    port=3306,
    charset='utf8mb4'
)

# 公共基类,统一绑定数据库
class BaseModel(Model):
    class Meta:
        database = db

四、核心:动态模型工厂函数

def create_dynamic_model(table_name: str) -> type:
    """
    根据表名动态创建 Peewee 模型类
    """
    # 动态创建 Meta 类
    Meta = type('Meta', (), {'table_name': table_name})

    # 模型字段定义
    attrs = {
        'id': AutoField(primary_key=True),
        'source_id': IntegerField(null=True),
        'index_name_one': CharField(max_length=255, null=True),
        'index_name_two': CharField(max_length=255, null=True),
        'Meta': Meta,
        '__module__': __name__,
    }

    # 动态类名,方便调试
    class_name = f'IndexRecordPairs_{table_name}'
    return type(class_name, (BaseModel,), attrs)

五、使用示例:动态建表 + 增删查改

# 假设我们按年份分表
table_2023 = create_dynamic_model('index_record_pairs_2023')
table_2024 = create_dynamic_model('index_record_pairs_2024')

# 自动建表(仅不存在时创建)
db.create_tables([table_2023, table_2024])

# 插入数据
table_2023.create(
    source_id=1001,
    index_name_one='GDP',
    index_name_two='地区生产总值'
)

# 查询数据
for row in table_2023.select():
    print(row.id, row.index_name_one, row.index_name_two)

# 更新数据
table_2023.update(index_name_two='全省GDP').where(table_2023.source_id == 1001).execute()

# 删除数据
table_2023.delete().where(table_2023.source_id == 1001).execute()

六、优化建议:缓存模型类避免重复创建

_MODEL_CACHE = {}

def get_dynamic_model(table_name: str) -> type:
    if table_name not in _MODEL_CACHE:
        _MODEL_CACHE[table_name] = create_dynamic_model(table_name)
    return _MODEL_CACHE[table_name]

七、总结

功能 是否支持
动态表名 ✅ 通过动态创建模型类
ORM 接口 ✅ 完全保留
自动建表 db.create_tables()
事务支持 ✅ 复用 Peewee 事务
性能影响 ✅ 极小,模型类可缓存

结论
Peewee 虽然不支持“运行时改表名”,但借助 Python 的 type() 动态创建类,我们可以轻松实现 MySQL 动态表名 的需求,且代码简洁、可维护性强,非常适合分表、日志、审计等场景。


如需进一步扩展,可以结合:

  • 分表策略(按年月、哈希、用户 ID);
  • 自动迁移工具(如 pt-online-schema-change);
  • 连接池(playhouse.pool)提升并发性能。

希望这篇博客能帮你在实际项目中灵活运用 Peewee!

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐