sqlalchemyでテーブル定義クラスを自動生成する方法【sqlacodegenを使おう】

こんにちは!

seiです!

 

業務でsqlalchemyを使用しています。
システムの移行に当たって、既存DBからpythonのテーブル定義のクラスを生成する際に少しはまったので備忘録です。

 

今回紹介するやり方とは別に、「automap」を使ったやり方があるみたいです。sutomapはDBに通信してテーブルの情報をすべてmetadateオブジェクトに格納しそれをもとに

以下の点が気に入らなかったので今回は「sqlalchemy」を使いました。

  • relationやカラムなどの情報が確認しずらい
  • DB定義変更時に影響範囲が分かりずらい
  • DB接続時に毎回生成したmodel classが変わるので、バグの原因を特定しずらい

sqlalchemyでテーブル定義クラスを自動生成する方法

 

テーブル定義を自動で生成するために「sqlacodegen」というツールを使いました。

https://pypi.org/project/sqlacodegen/

 

ただ、sqlacodegenはsqlalchemyの1.3系までしか対応していません。今回僕が業務で使用していたsqlalchemyはバージョン2だったので、自動生成されたモデルを少し編集する必要がありました。

 

まずはインストール

 

pip install sqlacodegen

# sqlalchemyのバージョンを一時的に下げる
pip install sqlalchemy 13.0.0

 

テーブル定義を自動で生成します。
クライアントライブラリにpymysqlを使用している場合は以下のようになります。


sqlacodegen mysql+pymysql://ユーザ:パスワード@ホスト名/DB名 --outfile 適当なファイル名

# ssl接続したい場合は以下のようにすればよいです
sqlacodegen mysql+pymysql://ユーザ:パスワード@ホスト名/DB名?ssl_key=共通鍵へのパス&ssl_check_hostname=false --outfile 適当なファイル名

 

他のクライアントやssl接続のパラメータについて知りたい場合は、sqlalchemyのドキュメントに書いてあります。(sqlacodegenを参照しても書いてなかったので苦労しました)

https://docs.sqlalchemy.org/en/20/dialects/mysql.html#module-sqlalchemy.dialects.mysql.pymysql

 

sqlalchemyのバージョンを戻す

pip uninstall sqlacodegen
pip install sqlalchemy==2.0.19

生成したファイルをsqlalchemy2.0用に書き換える

 

このままだと、古いバージョンのテーブル定義になってしまいます。型付けを行いたい場合は以下のガイドを読んでみてください。

2.0に移行するにあたって移行ガイドがドキュメントに載っています。

 

インパクトが大きいものを一部抜粋して紹介します。

declarative_base() を DeclarativeBaseクラスに置き換える

 

from sqlalchemy.orm import DeclarativeBase


class Base(DeclarativeBase):
    pass

Columnをmapped_column()に置き換える

例えば以下のようにテーブル定義が出力された場合は

from sqlalchemy import create_engine, Column, Integer, String, Sequence, DateTime, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from datetime import datetime

Base = declarative_base()

class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, Sequence('user_id_seq'), primary_key=True)
    username = Column(String(50), unique=True, nullable=False)
    email = Column(String(100), unique=True, nullable=False)
    full_name = Column(String(100))
    hashed_password = Column(String(100))
    created_at = Column(DateTime, default=datetime.utcnow)

# ... 他のモデル

以下のように変更します。

from sqlalchemy import create_engine, Integer, String, Sequence, DateTime, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import mapped_column
from datetime import datetime

Base = declarative_base()

class User(Base):
    __tablename__ = 'users'
    id = mapped_column(Integer, Sequence('user_id_seq'), primary_key=True)
    username = mapped_column(String(50), unique=True, nullable=False)
    email = mapped_column(String(100), unique=True, nullable=False)
    full_name = mapped_column(String(100))
    hashed_password = mapped_column(String(100))
    created_at = mapped_column(DateTime, default=datetime.utcnow)

# ... 他のモデル

pythonの型付けを行う

Mappedを利用して型付けとチェックが行えます。厳密に型を付けたいときに便利です。

 

from sqlalchemy import create_engine, Integer, String, Sequence, DateTime, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import mapped_column, Mapped
from datetime import datetime

Base = declarative_base()

class User(Base):
    __tablename__ = 'users'
    id:Mapped[int] = mapped_column(Integer, Sequence('user_id_seq'), primary_key=True)
    username:Mapped[str] = mapped_column(String(50), unique=True, nullable=False)
    email = mapped_column(String(100), unique=True, nullable=False)
    full_name = mapped_column(String(100))
    hashed_password = mapped_column(String(100))
    created_at = mapped_column(DateTime, default=datetime.utcnow)

# ... 他のモデル
DBからテーブルのclassを生成している画像
プログラミング学習方法を発信してます!