SqlAlchemy TIMESTAMP 'при обновлении' дополнительно

Я использую SqlAlchemy на python3.4.3 для управления базой данных MySQL. Я создавал таблицу с:

from datetime import datetime

from sqlalchemy import Column, text, create_engine
from sqlalchemy.types import TIMESTAMP
from sqlalchemy.dialects.mysql import BIGINT
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()
class MyClass(Base):

  __tablename__ = 'my_class'

  id = Column(BIGINT(unsigned=True), primary_key=True)
  created_at = Column(TIMESTAMP, default=datetime.utcnow, nullable=False)
  updated_at = Column(TIMESTAMP, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False)
  param1 = Column(BIGINT(unsigned=True), server_default=text('0'), nullable=False)

когда я создаю эту таблицу с помощью:

engine = create_engine('{dialect}://{user}:{password}@{host}/{name}'.format(**utils.config['db']))
Base.metadata.create_all(engine)

Я получил:

mysql> describe my_class;
+----------------+---------------------+------+-----+---------------------+-----------------------------+
| Field          | Type                | Null | Key | Default             | Extra                       |
+----------------+---------------------+------+-----+---------------------+-----------------------------+
| id             | bigint(20) unsigned | NO   | PRI | NULL                | auto_increment              |
| created_at     | timestamp           | NO   |     | CURRENT_TIMESTAMP   | on update CURRENT_TIMESTAMP |                           |
| updated_at     | timestamp           | NO   |     | 0000-00-00 00:00:00 |                             |
| param1         | bigint(20) unsigned | NO   |     | 0                   |                             |

Теперь проблема в том, что я не хочу, чтобы в моем атрибуте created_at использовалось значение по умолчанию для сервера on_update, его цель, по сути, записывать только при создании записи, а не при каждом обновлении, как указано в объявлении класса.

Из нескольких выполненных мною тестов я заметил, что если я вставлю другой атрибут типа TIMESTAMP перед created_at, то этот атрибут получит on update CURRENT_TIMESTAMP extra, а created_at - нет, как хотелось бы. Это говорит о том, что первый TIMESTAMP атрибут, который SqlAlchemy находит в объявлении сопоставления, получает on update CURRENT_TIMESTAMP дополнительно, хотя я не вижу причин для такого поведения.

Я также пробовал:

created_at = Column(TIMESTAMP, default=datetime.utcnow, server_onupdate=None, nullable=False)

а также

created_at = Column(TIMESTAMP, default=datetime.utcnow, server_onupdate=text(''), nullable=False)

но проблема не устранена. Любое предложение?


person Simone Bronzini    schedule 16.11.2015    source источник


Ответы (2)


По-видимому, проблема связана не с SqlAlchemy, а с базовым движком MySQL. По умолчанию on update CURRENT_TIMESTAMP устанавливается в первом столбце TIMESTAMP в таблице.

Это поведение описано здесь. Насколько я понимаю, возможное решение - запустить MySQL с флагом --explicit_defaults_for_timestamp=FALSE. Другое решение можно найти здесь. Я еще не пробовал ни одно из решений, я обновлю этот ответ, как только решу проблему.

РЕДАКТИРОВАТЬ: Я пробовал второй метод, и он не очень удобен, но работает. В моем случае я создал набор таблиц, у которых нет атрибута created_at, а затем изменил все остальные таблицы, как описано в приведенной выше ссылке.

Что-то вроде:

_no_alter = set(['tables', 'which', 'do not', 'have', 'a created_at', 'column'])
Base.metadata.create_all(engine)
for table in Base.metadata.tables.keys():
    if table not in _no_alter:
      engine.execute(text('ALTER TABLE {} MODIFY created_at TIMESTAMP NOT NULL DEFAULT 0'.format(table)))

EDIT2: еще один (более простой) способ добиться этого - установить в SqlAlchemy значение server_default для столбца:

created_at = Column(TIMESTAMP, default=datetime.utcnow, nullable=False, server_default=text('0'))
person Simone Bronzini    schedule 16.11.2015

Я столкнулся с той же проблемой, вы можете сделать это:

created_time   = Column(TIMESTAMP, nullable=False, server_default=func.now())
updated_time   = Column(TIMESTAMP, nullable=False, server_default=text('CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP'))
person Aashutosh jha    schedule 28.11.2019