よくある実装と発生する問題
SQLAlchemyとalembicを使用している場合、モデルのベースを作り、下記のように継承させる方法をよくとると思います。
base.py
from sqlalchemy import Column, DATETIME, Integer, func
class ModelBase:
id = Column(Integer, primary_key=True)
created_at = Column(DATETIME, nullable=False, server_default=func.now())
updated_at = Column(
DATETIME,
nullable=False,
server_default=func.now(),
onupdate=func.now(),
)
user.py
from sqlalchemy import Column, String
from app.db.base_class import Base
from app.models.base import ModelBase
class User(Base, ModelBase):
__tablename__ = "users"
name = Column(String(255), nullable=False)
しかし、上記の実装では、
name, id, created_at, updated_at
の順に作成されてしまいます。
使えなくはないですが、なんだかしっくりこないですね。
実際に、migrationファイルも下記のように書かれています。
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('users',
sa.Column('name', sa.String(length=255), nullable=False),
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('created_at', sa.DATETIME(), server_default=sa.text('now()'), nullable=False),
sa.Column('updated_at', sa.DATETIME(), server_default=sa.text('now()'), nullable=False),
sa.PrimaryKeyConstraint('id')
)
# ### end Alembic commands ###
対処法
下記のように、mapped_column
とsort_order
を使用することで、順番を正しいものにすることができます。
base.py
from sqlalchemy import DATETIME, Integer, func
from sqlalchemy.orm import mapped_column
class ModelBase:
id = mapped_column(Integer, primary_key=True, sort_order=-10)
created_at = mapped_column(
DATETIME, nullable=False, server_default=func.now(), sort_order=98
)
updated_at = mapped_column(
DATETIME,
nullable=False,
server_default=func.now(),
onupdate=func.now(),
sort_order=99,
)