You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何FreezeGun无法作用于SQLAlchemy默认值?

问题分析与解决方案

你遇到的问题核心是:Freezegun已经成功patch了Python层面的datetime.datetime.now(),但SQLAlchemy模型的created字段最终存储的却是数据库系统时间,而非冻结后的时间。下面是具体原因和解决办法:

根本原因

问题出在数据库层面的默认值覆盖了SQLAlchemy在Python侧生成的冻结时间,或者SQLAlchemy没有正确应用Python侧的默认值,导致数据库自动用自身系统时间填充字段。

具体拆解:

  1. 你的模型定义中,created字段设置了Python侧的默认值default=datetime.datetime.now(写法正确,传递的是函数对象而非调用结果),理论上SQLAlchemy会在插入数据前调用这个函数生成冻结后的时间。
  2. 但测试结果显示数据库存储的是系统时间,说明插入时SQLAlchemy没有传递Python生成的冻结值,而是数据库自动填充了自身的当前时间。
  3. 查看你的迁移文件,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:重新生成匹配模型的迁移文件

如果你的迁移文件是手动修改过的,建议重新生成正确的迁移文件,确保模型与数据库结构一致:

  1. 删除现有问题迁移文件
  2. 运行alembic revision --autogenerate -m "add my_object table"重新生成迁移
  3. 运行alembic upgrade head同步数据库结构

重新生成的迁移文件不会包含数据库层面的默认值(因为模型用的是Python侧默认),从而避免数据库自动填充时间的问题。

内容的提问来源于stack exchange,提问作者Saqib Ali

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:41:03