0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Python(Django) x AWS 一人アドベントカレンダーAdvent Calendar 2024

Day 23

【Python】AlembicでDateTime型のデフォルト値を挿入時の現在時刻にする方法

Posted at

概要

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

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?