SqlAlchemy TIMESTAMP 'on update' 额外

SqlAlchemy TIMESTAMP 'on update' extra

我在 python3.4.3 上使用 SqlAlchemy 来管理 MySQL 数据库。我正在创建一个 table 具有:

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)

当我创建此 table 时:

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                   |                             |

现在的问题是我不希望任何 on_update 服务器默认我的 created_at 属性,它的目的实际上是只在创建记录时写入,而不是在每次更新,如 class.

的声明中所述

从我所做的几个测试中,我注意到如果我在 created_at 之前插入另一个 TIMESTAMP 类型的属性,那么这个属性会得到额外的 on update CURRENT_TIMESTAMP,而 created_at 没有,如所愿。这表明 SqlAlchemy 在映射声明中找到的第一个 TIMESTAMP 属性获得了 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)

但问题依然存在。 有什么建议吗?

显然问题与 SqlAlchemy 无关,而与底层 MySQL 引擎有关。默认行为是在 table.

中的第一个 TIMESTAMP 列上设置 on update CURRENT_TIMESTAMP

描述了此行为 here. As far as I understand, a possible solution is to start MySQL with the --explicit_defaults_for_timestamp=FALSE flag. Another solution can be found here。我还没有尝试过任何一种解决方案,我会在解决问题后立即更新此答案。

编辑:我尝试了第二种方法,虽然不是很方便,但很管用。在我的例子中,我创建了一组没有 created_at 属性的 table,然后我按照上面 link 中的描述更改了所有剩余的 table .

大致如下:

_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'))

我遇到了同样的问题,您可以通过以下方式解决:

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'))