为什么 FreezeGun 不能使用 SQLAlchemy 默认值?
Why doesn't FreezeGun work with SQLAlchemy default values?
我使用以下模型创建了一个 SQLAlchemy 应用程序:
class MyObject(db.Model):
__tablename__ = 'my_object'
id = db.Column(db.Integer, nullable=False, primary_key=True, autoincrement=True)
some_string = db.Column(db.String(20), nullable=False)
created = db.Column(db.DateTime, default=datetime.datetime.now)
updated = db.Column(db.DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now)
我创建了这个迁移文件来配合它:
"""
Junk
Revision ID: 4b6fffffffff_
Revises: 4b6e7775856f
Create Date: 2019-11-08 00:31:13.297355
"""
# revision identifiers, used by Alembic.
revision = '4b6fffffffff_'
down_revision = '4b6e7775856f'
from alembic import op
import sqlalchemy as sa
import sqlalchemy_utils
from sqlalchemy import false
def upgrade():
op.create_table(
'my_object',
sa.Column('created', sa.DateTime(), nullable=False),
sa.Column('updated', sa.DateTime(), nullable=False),
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('some_string', sa.String(length=20), nullable=False),
sa.PrimaryKeyConstraint('id')
)
现在我有了这个使用 Freezegun:
的单元测试用例
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)
freezer = freeze_time(now_time)
freezer.start()
print 'datetime.datetime.now() = {}'.format(datetime.datetime.now())
m = MyObject(some_string="Hello World")
db.session.add(m)
db.session.commit()
freezer.stop()
print 'm.created = {}'.format(m.created)
这个测试用例产生这个输出:
datetime.datetime.now() = 2012-04-01 05:12:32.000543
m.created = 2019-11-09 04:04:55
为什么 m.created
当前挂钟时间而不是 FreezeGun 时间??两者应该是一样的。
根据 this answer,Freezegun 补丁 datetime.datetime.now()
。我的测试用例证实了这一点。那么 why/how 是存储在数据库中的不同值吗??
由于 MyObject
存在于模块命名空间中,因此它及其 class 级别属性在编译时进行评估。这发生在 Freezegun 修补 datetime.datetime.now
之前,因此列默认函数仍然指向 stdlib 实现。
这是一个更简单的例子:
import datetime
from freezegun import freeze_time
class MyObject:
dt_now = datetime.datetime.now
now_time = datetime.datetime(
year=2012, month=4, day=1, hour=5, minute=12, second=32, microsecond=543
)
freezer = freeze_time(now_time)
freezer.start()
print(datetime.datetime.now()) # 2012-04-01 05:12:32.000543
print(MyObject().dt_now()) # 2019-11-09 15:44:29.437382
freezer.stop()
现在在设置 class 属性之前实例化 Freezegun:
now_time = datetime.datetime(
year=2012, month=4, day=1, hour=5, minute=12, second=32, microsecond=543
)
freezer = freeze_time(now_time)
freezer.start()
class MyObject:
dt_now = datetime.datetime.now
print(datetime.datetime.now()) # 2012-04-01 05:12:32.000543
print(MyObject().dt_now()) # 2012-04-01 05:12:32.000543
freezer.stop()
引用 this issue,将您的默认值包装在 lambda 中有效:
created = db.Column(db.DateTime, default=lambda: datetime.datetime.now())
...因为这会阻止 stdlib 函数绑定到 Column 默认值。或者,确保在设置 Freezegun 后导入 MyObject
所在的模块。
我使用以下模型创建了一个 SQLAlchemy 应用程序:
class MyObject(db.Model):
__tablename__ = 'my_object'
id = db.Column(db.Integer, nullable=False, primary_key=True, autoincrement=True)
some_string = db.Column(db.String(20), nullable=False)
created = db.Column(db.DateTime, default=datetime.datetime.now)
updated = db.Column(db.DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now)
我创建了这个迁移文件来配合它:
"""
Junk
Revision ID: 4b6fffffffff_
Revises: 4b6e7775856f
Create Date: 2019-11-08 00:31:13.297355
"""
# revision identifiers, used by Alembic.
revision = '4b6fffffffff_'
down_revision = '4b6e7775856f'
from alembic import op
import sqlalchemy as sa
import sqlalchemy_utils
from sqlalchemy import false
def upgrade():
op.create_table(
'my_object',
sa.Column('created', sa.DateTime(), nullable=False),
sa.Column('updated', sa.DateTime(), nullable=False),
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('some_string', sa.String(length=20), nullable=False),
sa.PrimaryKeyConstraint('id')
)
现在我有了这个使用 Freezegun:
的单元测试用例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)
freezer = freeze_time(now_time)
freezer.start()
print 'datetime.datetime.now() = {}'.format(datetime.datetime.now())
m = MyObject(some_string="Hello World")
db.session.add(m)
db.session.commit()
freezer.stop()
print 'm.created = {}'.format(m.created)
这个测试用例产生这个输出:
datetime.datetime.now() = 2012-04-01 05:12:32.000543
m.created = 2019-11-09 04:04:55
为什么 m.created
当前挂钟时间而不是 FreezeGun 时间??两者应该是一样的。
根据 this answer,Freezegun 补丁 datetime.datetime.now()
。我的测试用例证实了这一点。那么 why/how 是存储在数据库中的不同值吗??
由于 MyObject
存在于模块命名空间中,因此它及其 class 级别属性在编译时进行评估。这发生在 Freezegun 修补 datetime.datetime.now
之前,因此列默认函数仍然指向 stdlib 实现。
这是一个更简单的例子:
import datetime
from freezegun import freeze_time
class MyObject:
dt_now = datetime.datetime.now
now_time = datetime.datetime(
year=2012, month=4, day=1, hour=5, minute=12, second=32, microsecond=543
)
freezer = freeze_time(now_time)
freezer.start()
print(datetime.datetime.now()) # 2012-04-01 05:12:32.000543
print(MyObject().dt_now()) # 2019-11-09 15:44:29.437382
freezer.stop()
现在在设置 class 属性之前实例化 Freezegun:
now_time = datetime.datetime(
year=2012, month=4, day=1, hour=5, minute=12, second=32, microsecond=543
)
freezer = freeze_time(now_time)
freezer.start()
class MyObject:
dt_now = datetime.datetime.now
print(datetime.datetime.now()) # 2012-04-01 05:12:32.000543
print(MyObject().dt_now()) # 2012-04-01 05:12:32.000543
freezer.stop()
引用 this issue,将您的默认值包装在 lambda 中有效:
created = db.Column(db.DateTime, default=lambda: datetime.datetime.now())
...因为这会阻止 stdlib 函数绑定到 Column 默认值。或者,确保在设置 Freezegun 后导入 MyObject
所在的模块。