Неожиданная временная метка SQLAlchemy и PostgreSQL с onupdate=func.now()

В следующем коде после 5-секундного сна я ожидаю, что изменится вторая часть date_updated, но изменится только миллисекундная часть. Если я использую database_url = 'sqlite:///:memory:', он работает так, как ожидалось. Почему?

class Base(MappedAsDataclass, DeclarativeBase):
    pass


class Test(Base):
    __tablename__ = 'test'

    test_id: Mapped[int] = mapped_column(primary_key=True, init=False)
    name: Mapped[str]
    date_created: Mapped[datetime] = mapped_column(
        TIMESTAMP(timezone=True),
        insert_default=func.now(),
        init=False
    )
    date_updated: Mapped[datetime] = mapped_column(
        TIMESTAMP(timezone=True),
        nullable=True,
        insert_default=None,
        onupdate=func.now(),
        init=False
    )


database_url: URL = URL.create(
    drivername='postgresql+psycopg',
    username='my_username',
    password='my_password',
    host='localhost',
    port=5432,
    database='my_db'
)
engine = create_engine(database_url, echo=True)
Base.metadata.drop_all(engine)
Base.metadata.create_all(engine)

with Session(engine) as session:
    test = Test(name='foo')
    session.add(test)
    session.commit()

    print(test)
    
    time.sleep(5)

    test.name = 'bar'    
    session.commit()

    print(test.date_created.time()) # prints: 08:07:45.413737
    print(test.date_updated.time()) # prints: 08:07:45.426483
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
3
0
54
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Функция now() PostgreSQL возвращает время начала текущей транзакции. В коде в вопросе вторая транзакция запускается оператором print, поскольку необходимо открыть новую транзакцию для получения значений, срок действия которых истек предыдущей commit. Таким образом, разница в наблюдаемых временных метках составляет всего несколько миллисекунд.

Чтобы получить желаемое поведение, любой из этих подходов должен работать:

  • Не истекайте при фиксации (но внимательно учтите последствия в рабочем коде):
    with orm.Session(engine, expire_on_commit=False) as session:
    
  • удалить звонок print()
  • Позвоните session.comit() еще раз после звонка time.sleep(), чтобы начать новую транзакцию.

В SQLite нет функции now(); SQLAlchemy преобразует вызов функции в

SELECT CURRENT_TIMESTAMP AS now

SQLite, похоже, не ограничивает значение CURRENT_TIMESTAMP началом текущей транзакции:

sqlite> begin;
sqlite> select current_timestamp as 'now';
2024-05-25 13:21:22
sqlite> select current_timestamp as 'now';
2024-05-25 13:21:24
sqlite> select current_timestamp as 'now';
2024-05-25 13:21:26
sqlite> rollback;

Другие вопросы по теме