概要
Pythonアプリで、SQLAlchemyとAlembicを利用してMySQLのDBを利用しています。
DateTime
型のカラムのデフォルト値をレコード作成時の「現在時刻」にする方法が、いくつかあったので紹介します。
前提
db:mysql8.0
python:3.12
alembic:1.13.2
SQLAlchemy:2.0.32
①server_default=func.now()
を指定する方法
以下のようなモデル定義があるとします。
class SampleTable(Base):
__tablename__ = 'sample_table'
id = Column(Integer, primary_key=True, autoincrement=True)
created_at = Column(DateTime, nullable=False, server_default=func.now())
上記のようにserver_default=func.now()
と指定すると、
マイグレーション(alembic revision --autogenerate
実行)時に生成されるマイグレーションファイルでは、
sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False)
と記載されました。
sa.text('now()'
と認識されるのですね。
基本、これで問題ないと思います。
func.now()
は、SQLAlchemyのfunc
モジュールを使用して、DBの現在時刻を取得する関数です。
Alembicのドキュメントにもserver_default
というパラメータと関数func.now()
を使っている記述がありました。
from alembic import op
from sqlalchemy import Column, TIMESTAMP, func
# specify "DEFAULT NOW" along with the column add
op.add_column(
"account",
Column("timestamp", TIMESTAMP, server_default=func.now()),
)
上記のように、公式ドキュメントでは、server_default=func.now()
を推奨しているようです。
ただ、ネットで調べてみると、他にも実現方法があるとのことで、ついでに検証してみました。
②server_default=text('now()')
を指定する方法
まずは以下。
マイグレーションファイルで生成されたtext('now()')
をそのままモデル定義で入れてみます。
class SampleTable(Base):
__tablename__ = 'sample_table'
id = Column(Integer, primary_key=True, autoincrement=True)
created_at = Column(DateTime, nullable=False, server_default=text('now()'))
結果、マイグレーションファイルには先ほどと同じようにserver_default=sa.text('now()')
が記載されていました。
sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False),
作成したテーブルに、試しにcreated_at
の値に何も入れずにレコードを挿入すると、現在時刻になっていることを確認できました。
直接SQLを指定してあげてもいいのですね。
③server_default=func.current_timestamp()
を指定する方法
また、current_timestamp
を使った方法もあるとのことで、以下のモデル定義にしてみます。
class SampleTable(Base):
__tablename__ = 'sample_table'
created_at = Column(DateTime, nullable=False, server_default=func.current_timestamp())
これでマイグレーションファイルを生成すると...
sa.Column('created_at', sa.DateTime(), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=False),
というようになりました。
先ほどとはマイグレーションファイルの記述方法が異なりますが、
こちらもレコード挿入時に現在時刻になっていることを確認できました。
ということで、結論、
上記で挙げた方法であれば、マイグレーション時に自動で現在時刻というように読み取ってくれるようです。
私は公式docにあるserver_default=func.now()
を採用しました。
備考
一点、注意点があるとすれば...
モデル定義で
class SampleTable(Base):
__tablename__ = 'sample_table'
created_at = Column(DateTime, nullable=False, default=func.now())
というように、server_default
ではなくdefault
とすると、これはデフォルトのオプションパラメータとして認識してくれません。このままマイグレートして、created_at
未指定でレコード挿入すると以下のエラーになります。
SQLエラー [1364] [HY000]: Field 'created_at' doesn't have a default value