7

我在 python3.4.3 上使用 SqlAlchemy 来管理 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额外的,而created_at不会如所愿。这表明TIMESTAMPSqlAlchemy 在映射声明中找到的第一个属性得到了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)

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

4

2 回答 2

7

显然问题与 SqlAlchemy 无关,而与底层 MySQL 引擎有关。默认行为是on update CURRENT_TIMESTAMP在表中的第一个 TIMESTAMP 列上设置。

此处描述了此行为。据我了解,一种可能的解决方案是使用--explicit_defaults_for_timestamp=FALSE标志启动 MySQL。可以在此处找到另一种解决方案。我还没有尝试过任何一种解决方案,我会在解决问题后立即更新这个答案。

编辑:我尝试了第二种方法,它不是很方便,但它有效。就我而言,我创建了一组没有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:另一种(更简单)的方法是在 SqlAlchemyserver_default中为该列设置一个值:

created_at = Column(TIMESTAMP, default=datetime.utcnow, nullable=False, server_default=text('0'))
于 2015-11-16T20:56:56.890 回答
7

我遇到了同样的问题,您可以通过以下方式完成此操作:

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'))
于 2019-11-28T04:23:25.587 回答