为何FreezeGun无法作用于SQLAlchemy默认值?
问题分析与解决方案
你遇到的问题核心是:Freezegun已经成功patch了Python层面的datetime.datetime.now(),但SQLAlchemy模型的created字段最终存储的却是数据库系统时间,而非冻结后的时间。下面是具体原因和解决办法:
根本原因
问题出在数据库层面的默认值覆盖了SQLAlchemy在Python侧生成的冻结时间,或者SQLAlchemy没有正确应用Python侧的默认值,导致数据库自动用自身系统时间填充字段。
具体拆解:
- 你的模型定义中,
created字段设置了Python侧的默认值default=datetime.datetime.now(写法正确,传递的是函数对象而非调用结果),理论上SQLAlchemy会在插入数据前调用这个函数生成冻结后的时间。 - 但测试结果显示数据库存储的是系统时间,说明插入时SQLAlchemy没有传递Python生成的冻结值,而是数据库自动填充了自身的当前时间。
- 查看你的迁移文件,
created字段被定义为sa.DateTime(), nullable=False但未设置任何默认值。这种情况下,如果数据库表实际存在数据库层面的默认值(比如你手动添加了CURRENT_TIMESTAMP),就会出现SQLAlchemy未传值时,数据库自动填充系统时间的情况。
解决方案
方案1:确保Python侧默认值生效,移除数据库层面默认值
- 确认模型定义正确:
created = db.Column(db.DateTime, default=datetime.datetime.now)(必须是不带括号的函数对象,不能写成datetime.datetime.now())。 - 检查数据库表结构,手动移除
created字段的数据库层面默认值(比如MySQL的CURRENT_TIMESTAMP),让SQLAlchemy的Python侧默认值完全接管。 - 测试时可以在
commit()前先打印m.created,验证是否已经是冻结后的时间:def test_freeze_gun_on_sql_alchemy(self): now_time = datetime.datetime(year=2012, month=4, day=1, hour=5, minute=12, second=32, microsecond=543) with freeze_time(now_time): print 'datetime.datetime.now() = {}'.format(datetime.datetime.now()) m = MyObject(some_string="Hello World") print 'm.created before commit = {}'.format(m.created) # 验证此处是否为冻结时间 db.session.add(m) db.session.commit() print 'm.created after commit = {}'.format(m.created)
方案2:改用with语句确保Freezegun全程生效
手动调用freezer.start()/stop()可能存在时机遗漏,改用with语句可以确保Freezegun在整个代码块内持续生效,覆盖SQLAlchemy处理默认值的所有环节:
def test_freeze_gun_on_sql_alchemy(self): now_time = datetime.datetime(year=2012, month=4, day=1, hour=5, minute=12, second=32, microsecond=543) with freeze_time(now_time): print 'datetime.datetime.now() = {}'.format(datetime.datetime.now()) m = MyObject(some_string="Hello World") db.session.add(m) db.session.commit() print 'm.created = {}'.format(m.created)
方案3:重新生成匹配模型的迁移文件
如果你的迁移文件是手动修改过的,建议重新生成正确的迁移文件,确保模型与数据库结构一致:
- 删除现有问题迁移文件
- 运行
alembic revision --autogenerate -m "add my_object table"重新生成迁移 - 运行
alembic upgrade head同步数据库结构
重新生成的迁移文件不会包含数据库层面的默认值(因为模型用的是Python侧默认),从而避免数据库自动填充时间的问题。
内容的提问来源于stack exchange,提问作者Saqib Ali
相关产品推荐
相关产品推荐

