Почему FreezeGun не работает со значениями по умолчанию SQLAlchemy? - PullRequest
1 голос
/ 09 ноября 2019

Я создал приложение 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время?? Два должны быть одинаковыми.

Согласно этому ответу , Freezegun исправляет datetime.datetime.now(). Мой тестовый пример подтверждает это. Итак, почему / как другое значение хранится в базе данных ??

1 Ответ

3 голосов
/ 09 ноября 2019

Поскольку MyObject живет в пространстве имен модуля, он и его атрибуты уровня класса оцениваются во время компиляции. Это происходит до того, как 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()

Теперь создайте экземпляр 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()

Ссылка на эту проблему , упаковка значений по умолчанию в лямбду работает:

created = db.Column(db.DateTime, default=lambda: datetime.datetime.now())

... так как это предотвращает привязку функции stdlib кСтолбец по умолчанию. Или убедитесь, что модуль, в котором находится MyObject, импортируется после установки Freezegun.

...