
peewee 的 mysql 数据库连接默认强制设置 `sql_mode="pipes_as_concat"`,会覆盖 mysql 全局 strict mode(如 `traditional`),导致 `null=false` 字段缺失时静默插入空值而非报错,破坏数据完整性保障。
在使用 Peewee 与 MySQL 配合时,一个容易被忽视但影响深远的问题是:即使 MySQL 已启用全局严格 SQL 模式(如 SET GLOBAL sql_mode = "TRADITIONAL"),Peewee 仍可能绕过该约束, silently 插入不完整记录。根本原因在于 Peewee 的 MySQLDatabase 类在初始化连接时,主动覆盖了 MySQL 的默认/全局 sql_mode,硬编码设为 "PIPES_AS_CONCAT"(见源码第 4147 行)。该设置虽兼容 Peewee 内部字符串拼接语法(如 + 操作符),却无意中清除了 STRICT_TRANS_TABLES、STRICT_ALL_TABLES 或 TRADITIONAL 等关键严格模式,致使数据库层无法对 NOT NULL 字段缺失、无默认值等情形抛出异常。
例如,定义如下模型:
from peewee import Model, MySQLDatabase, CharField, DateTimeField, BooleanField
debug_db = MySQLDatabase(
'debug_db',
user='DEBUG',
password='secret',
host='localhost'
)
class Person(Model):
first_name = CharField(32)
last_name = CharField(32, null=False) # 必填字段
signup_time = DateTimeField()
approved = BooleanField()
class Meta:
database = debug_db当仅传入部分字段创建实例并调用 .save() 时:
john = Person(first_name="John") john.save() # ✅ 无异常!但 MySQL 实际插入了 last_name='', signup_time='0000-00-00 00:00:00'
此时 MySQL 并未触发 Field 'last_name' doesn't have a default value 错误——因为 Peewee 连接的 sql_mode 已被重置,丢失了 strict 行为。
✅ 正确解决方案
方案一:显式指定严格 SQL 模式(推荐)
直接在数据库实例化时通过 sql_mode 参数覆盖 Peewee 默认值,显式启用你所需的严格模式:
debug_db = MySQLDatabase(
'debug_db',
user='DEBUG',
password='secret',
host='localhost',
sql_mode='TRADITIONAL' # 或 'STRICT_TRANS_TABLES,PIPES_AS_CONCAT'
)✅ TRADITIONAL 包含 STRICT_TRANS_TABLES + STRICT_ALL_TABLES + 其他安全模式,是兼顾兼容性与严格性的常用选择。 ⚠️ 注意:若需保留 PIPES_AS_CONCAT(如使用 + 拼接字段),请将其显式加入,例如 'STRICT_TRANS_TABLES,PIPES_AS_CONCAT'。
方案二:动态追加模式(高级场景)
若需完全继承 MySQL 全局配置并仅额外添加 PIPES_AS_CONCAT,可禁用 Peewee 的 sql_mode 覆盖,并通过 init_command 动态扩展:
debug_db = MySQLDatabase(
'debug_db',
user='DEBUG',
password='secret',
host='localhost',
sql_mode=None, # 关键:禁用 Peewee 默认覆盖
init_command="SET sql_mode=(SELECT CONCAT(@@sql_mode,',PIPES_AS_CONCAT'));"
)此方式确保全局 strict mode 生效,同时满足 Peewee 语法需求。
? 验证是否生效
连接后执行查询确认当前会话模式:
debug_db.connect()
cursor = debug_db.cursor()
cursor.execute("SELECT @@sql_mode")
print(cursor.fetchone()) # 应输出包含 'STRICT_TRANS_TABLES' 或 'TRADITIONAL' 的字符串? 注意事项
- SQLite 后端不受此影响,其 NOT NULL 约束由 Peewee 层主动校验并抛出 IntegrityError;
- MySQLConnectorDatabase(playhouse.mysql_ext)同样受此逻辑影响,需同样配置;
- 生产环境务必启用严格 SQL 模式,避免因静默转换(如空字符串 → 0、零日期)引发业务逻辑错误;
- Peewee 本身不会对 None 值做字段级非空校验(除非显式赋 None 触发 IntegrityError),数据完整性最终依赖数据库约束与 SQL 模式。
通过合理配置 sql_mode,你既能享受 Peewee 的开发效率,又能确保 MySQL 严守数据契约——这才是稳健 ORM 集成的关键实践。









