SQLAlchemyとAlembicを使った開発で、モデル定義とDBスキーマを一致させる autogenerate
が
Pythonのfloat
型をMySQLのFLOAT
/DOUBLE
型に意図した通りマッピングくれないケースがあるので注意。
Pythonのfloat
がMySQLのFLOAT
にマッピングされる問題
Pythonの標準float
型は64bitの倍精度浮動小数点数であり、MySQLのDOUBLE
型に相当します。
しかし、SQLAlchemyで以下のように定義したモデルから生成されたテーブルでは、amount
カラムが32bitの単精度FLOAT
型になってしまってたんですよね。これでは有効数字が約7桁しかなく、高精度な数値が扱えません(というかバグとして報告されてしまっていました)。
# models.py
from sqlalchemy.orm import Mapped, mapped_column
class Transaction(Base):
__tablename__ = 'transactions'
# ...
amount: Mapped[float] = mapped_column(nullable=False)
autogenerate
が変更を検出しない
このカラムの型を修正するため、モデル定義を倍精度を意図するFloat(precision=53)
や、より明示的なMySQL方言のmysql.DOUBLE
に変更してみました。
# 変更案1: Float(precision=53)
from sqlalchemy import Float
amount: Mapped[float] = mapped_column(Float(precision=53), nullable=False)
# 変更案2: mysql.DOUBLE
from sqlalchemy.dialects.mysql import DOUBLE
amount: Mapped[float] = mapped_column(DOUBLE, nullable=False)
期待ではこれに対してマイグレーションを作成すると差異を検出してくれると思っていました。しかし、どちらのケースでalembic revision --autogenerate
を実行しても、Alembicは変更を検出しなかったのです。
これは、Alembicのデフォルトの型比較ロジックが、DB上の既存のFLOAT
型とこれらの型指定を「差異なし」と判断してしまうことがあるためらしいんですね。
解決策:マイグレーションファイルの手動作成
autogenerate
が機能しない以上、手動でマイグレーションファイルを作成し、直接SQLの変更を記述する方法に切り替えました。
1. 空のマイグレーションファイルを生成
まず、Alembicコマンドでマイグレーションファイルの雛形を作成します。
alembic revision -m "change_amount_column_from_float_to_double"
2. マイグレーションファイルに直接変更を記述
生成されたversions/xxxxxxxx_....py
ファイルを編集し、upgrade
とdowngrade
関数にop.alter_column
を記述します。
"""change amount column from float to double
Revision ID: xxxxxxxx
Revises: yyyyyyyy
Create Date: 2025-09-16 01:13:22.518320
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import mysql
# revision identifiers, used by Alembic.
revision = 'xxxxxxxx'
down_revision = 'yyyyyyyy'
branch_labels = None
depends_on = None
def upgrade() -> None:
op.alter_column('transactions', 'amount',
existing_type=mysql.FLOAT(),
type_=mysql.DOUBLE(),
nullable=False)
def downgrade() -> None:
op.alter_column('transactions', 'amount',
existing_type=mysql.DOUBLE(),
type_=mysql.FLOAT(),
nullable=False)
この方法であれば、Alembicの自動検出ロジックに依存せず、意図したスキーマ変更を確実に実行できました。
実はほかにも型を厳密にすることで検出できるかもしれないんでしすが、今回のケースではほかの個所で厳密にしてほしくないケースがあったのでこのやり方に落ち着きました。