SQLAlchemy по умолчанию DateTime

Это моя декларативная модель:

import datetime
from sqlalchemy import Column, Integer, DateTime
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class Test(Base):
    __tablename__ = 'test'

    id = Column(Integer, primary_key=True)
    created_date = DateTime(default=datetime.datetime.utcnow)

Однако, когда я пытаюсь импортировать этот модуль, я получаю эту ошибку:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "orm/models2.py", line 37, in <module>
    class Test(Base):
  File "orm/models2.py", line 41, in Test
    created_date = sqlalchemy.DateTime(default=datetime.datetime.utcnow)
TypeError: __init__() got an unexpected keyword argument 'default'

Если я использую тип Integer, я могу установить значение по умолчанию. Что происходит?

Ответ 1

DateTime не имеет ключа по умолчанию в качестве входа. Клавиша по умолчанию должна быть введена в функцию Column. Попробуйте следующее:

import datetime
from sqlalchemy import Column, Integer, DateTime
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class Test(Base):
    __tablename__ = 'test'

    id = Column(Integer, primary_key=True)
    created_date = Column(DateTime, default=datetime.datetime.utcnow)

Ответ 2

Вычислять метки времени в вашей БД, а не в вашем клиенте

Для здравомыслия вы, вероятно, хотите, чтобы все datetimes рассчитывались вашим сервером БД, а не сервером приложений. Вычисление метки времени в приложении может привести к проблемам, поскольку задержка в сети является переменной, клиенты испытывают немного различный сдвиг тактовой частоты, а разные языки программирования иногда вычисляют время немного по-разному.

SQLAlchemy позволяет вам сделать это, передавая func.now() или func.current_timestamp() (они являются псевдонимами друг друга), который указывает БД рассчитать саму метку времени.

Использовать SQLALchemy server_default

Кроме того, для значения по умолчанию, когда вы уже говорите БД для вычисления значения, обычно лучше использовать server_default вместо default по default. Это говорит SQLAlchemy передать значение по умолчанию как часть оператора CREATE TABLE.

Например, если вы напишите специальный скрипт для этой таблицы, использование server_default означает, что вам не нужно беспокоиться о ручном добавлении вызова метки времени в ваш скрипт - база данных установит его автоматически.

Понимание SQLAlchemy onupdate/server_onupdate

SQLAlchemy также поддерживает onupdate поэтому при onupdate обновлении строки вставляется новая timestamp. Опять же, лучше всего сказать БД рассчитать саму метку времени:

from sqlalchemy.sql import func

time_created = Column(DateTime(timezone=True), server_default=func.now())
time_updated = Column(DateTime(timezone=True), onupdate=func.now())

Существует параметр server_onupdate, но, в отличие от server_default, он фактически ничего не устанавливает на server_default. Он просто сообщает SQLalchemy, что ваша база данных изменит столбец при обновлении (возможно, вы создали триггер для столбца), поэтому SQLAlchemy запросит возвращаемое значение, чтобы он мог обновить соответствующий объект.

Еще один потенциальный вопрос:

Вы можете быть удивлены, заметив, что если вы сделаете кучу изменений в одной транзакции, они все будут иметь одну и ту же метку времени. Это потому, что стандарт SQL указывает, что CURRENT_TIMESTAMP возвращает значения, основанные на начале транзакции.

PostgreSQL обеспечивает не-SQL стандарта statement_timestamp() и clock_timestamp(), которые меняются в рамках транзакции. Документы здесь: https://www.postgresql.org/docs/current/static/functions-datetime.html#FUNCTIONS-DATETIME-CURRENT

Метка времени UTC

Если вы хотите использовать метки времени UTC, заглушка реализации func.utcnow() предоставляется в документации по SQLAlchemy. Вы должны обеспечить соответствующие специфичные для драйвера функции самостоятельно, хотя.

Ответ 3

Вы также можете использовать встроенную функцию sqlalchemy для DateTime по умолчанию

from sqlalchemy.sql import func

DT = Column(DateTime(timezone=True), default=func.now())

Ответ 4

Параметр ключевого слова default должен быть присвоен объекту Column.

Пример:

Column(u'timestamp', TIMESTAMP(timezone=True), primary_key=False, nullable=False, default=time_now),

Значение по умолчанию может быть вызываемым, которое здесь определяется следующим образом.

from pytz import timezone
from datetime import datetime

UTC = timezone('UTC')

def time_now():
    return datetime.now(UTC)

Ответ 5

Скорее всего, вы захотите использовать onupdate=datetime.now, чтобы ОБНОВЛЕНИЯ также меняли поле last_updated.

SQLAlchemy имеет два значения по умолчанию для функций, выполняемых на Python.

  • default устанавливает значение на INSERT, только один раз
  • onupdate также устанавливает значение для вызываемого результата в UPDATE.

Ответ 6

Согласно документации PostgreSQL, https://www.postgresql.org/docs/9.6/static/functions-datetime.html.

now, CURRENT_TIMESTAMP, LOCALTIMESTAMP return the time of transaction.

Это считается особенностью: цель состоит в том, чтобы позволить одной транзакции иметь согласованное представление о "текущем" времени, так что несколько модификаций в рамках одной транзакции имеют одинаковую отметку времени.

Вы можете использовать statement_timestamp или clock_timestamp, если вы не хотите транзакций метки времени.

statement_timestamp()

возвращает время начала текущего оператора (точнее, время получения последнего командного сообщения от клиента). statement_timestamp

clock_timestamp()

возвращает текущее текущее время, и, следовательно, его значение изменяется даже в пределах одной команды SQL.