Open2

pythonでmysqlとalembicとsqlalchemyを使う

masaobluemasaoblue

mysqlのコンテナを用意

このあたりを参考にさせて貰った

https://zenn.dev/mstn_/articles/ad5d7c7ad7e2d6

compose.yml
version: "3"
services:
  mysql:
    image: mysql:8.1.0
    ports:
      - 9002:3306 # portは適宜変更
    environment:
      MYSQL_ROOT_PASSWORD: mysql
      MYSQL_DATABASE: db
      MYSQL_USER: user
      MYSQL_PASSWORD: password
    volumes:
      - type: bind
        source: mysql-data
        target: /var/lib/mysql
        consistency: delegated

  db-cli:
    image: mysql:8.1.0
    command: mysql -hmysql -uuser -ppassword db

volumes:
  mysql-data:
  • docker compose up -d で起動
  • docker compose run db-cli でmysqlのcliにログイン

関連ライブラリインストール

requirements.txt
alembic
pymysql
mysqlclient

pip install -r requirements.txt でインストール

sqlalchemyのmodelを定義し、それを元にmigrationファイル作成

基本は以下を参考に設定

https://zenn.dev/shimakaze_soft/articles/4c0784d9a87751

動かなかった所1

自分の手元だとenv.pyに

env.py
from settings import Base
target_metadata = Base.metadata

を記述してもmodelが認識されなかったので、もう一つの方法として書かれている

env.py
target_metadata = [
    User.metadata
]

こちらを使用したところちゃんと認識された。

動かなかった所2

settings.pyの中で

settings.py
Engine = create_engine(
    path,
    encoding="utf-8",
    echo=False
)

としているが、encodingを指定するとalembicコマンド実行時に以下のエラーが出た。

TypeError: Invalid argument(s) 'encoding' sent to create_engine(), using configuration MySQLDialect_pymysql/QueuePool/Engine.  Please check that the keyword arguments are appropriate for this combination of components.

以下を見るとencodingという引数は無さそう。

https://docs.sqlalchemy.org/en/20/core/engines.html

代わりに接続文字列(path)の末尾にquery stringを付ける方法が使えそうなのでとりあえず指定しておく。(おそらくバージョンが新しくなって削除されたものと思われるが、これで同じ設定にできているのかは不明)

settings.py
path = "mysql+pymysql://user:password@localhost:9002/db??charset=utf8"

このあたりはあまり理解してないので後でsqlalchemy側のドキュメントを見ておくべき。

マイグレーション実行

alembic upgrade head を実行したら以下のエラー

sqlalchemy.exc.CompileError: (in table 'users', column 'name'): VARCHAR requires a length on dialect mysql

このあたりにある通り、mysqlの場合はString型のlengthを省略できないよという感じらしい

https://github.com/sqlalchemy/alembic/issues/912

# before
sa.Column('name', sa.String(), nullable=True),
# after 
sa.Column('name', sa.String(length=256), nullable=True),

無事に通るようになった

$ alembic upgrade head
INFO  [alembic.runtime.migration] Context impl MySQLImpl.
INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
INFO  [alembic.runtime.migration] Running upgrade  -> 3b81cda1fd89, create tables

以下を実行し、usersテーブルが作られていることを確認

docker compose run db-cli

mysql> show tables;
+-----------------+
| Tables_in_db    |
+-----------------+
| alembic_version |
| users           |
+-----------------+
2 rows in set (0.00 sec)
masaobluemasaoblue

strawberryを使ってGraphQLのAPIを作りたかった。

でもsqlalchemyで定義したmodelクラスに @strawberry.type を付与すると ❌ Type User must define one or more fields. みたいなエラーになってしまった。

自分で再定義しなくて良い方法はあるのかなと調べてみたら、一応以下のライブラリが使えた。

https://github.com/strawberry-graphql/strawberry-sqlalchemy

from strawberry_sqlalchemy_mapper import StrawberrySQLAlchemyMapper

import models # sqlalchemyのmodel定義

strawberry_sqlalchemy_mapper = StrawberrySQLAlchemyMapper()

@strawberry_sqlalchemy_mapper.type(models.User) # @strawberry.type の代わりにこれを付与
class User:
    # ここにfieldなどを自分で定義しなくて済む
    pass

とても便利だけどスターの数とか見る限りはちゃんと運用していけるのか若干不安。
とはいえ個人開発用なので今回は採用してみようと思う。