2019-04-05に更新

SQLAlchemy の Declarative API を使ってハマった事

Python3 + Bottle + Jinja2 + SQLAlchemy で Web サービスを作っているのですが、SQLAlchemy の Declarative API を使い始めた時にちょっとした失敗をやらかしましたので、この辺りで供養しておきます。

以下は SQLAlchemy の Declarative API を使って User クラス(users テーブル) を定義し、テーブルを生成するコードです。コードが膨れ上がるのが嫌いなので、User クラスは user.py に、テーブル生成は database.py に書いています。しかし、database.py の create_all() を読んでもテーブルが生成されない、という問題にぶつかりました。

# user.py
from sqlalchemy import Column
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.types import String

Base = declarative_base()

class User(Base):
__tablename__ = "users"
user_id = Column(UUID, primary_key=True)
name = Column(String)
     :
# database.py
from sqlalchemy.ext.declarative import declarative_base
from .user import User

Base = declarative_base()
Base.metadata.create_all(bind=self.engine)
# ここで users テーブルが作られているはずが、作られない

何が問題なのかというと、複数のソースに分けて書いた時の対応がまずかったのです。
ネットでよく見かける Delcarative API の サンプルは同じ py ファイルの中に書いてあるので問題はありません。
上の例の場合、user.py と database.py の双方で declarative_base() を呼び出して基底クラス Base を使用していますが、互いに独立したメタクラスを触っているので、テーブル生成しようにも Users クラスで定義した内容が引き渡されていませんでした。

解決方法としては以下のようになります。declarative_base(0 で生成した Base を引き回すため、独立した base.py を生成し、user.py を database.py の双方から参照するように修正しました。

# base.py
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()
# user.py
from .base import Base
from sqlalchemy import Column
from sqlalchemy.types import String

class User(Base):
__tablename__ = "users"
   :
# database.py
from .base import Base
from .user import User

Base.metadata.create_all(bind=self.engine)
# ここで users テーブルが作られる

以上の対応で、無事 users テーブルが生成されるようになりました。よかったよかった。

しかしこの Declarative API 、実に ORM らしい使い方になるのでとても快適ですね。
当初 SQLAlchemy のリファレンスを適当に見ながら書いたものは Reflective Table Object という方法だったようで
クエリの結果がdict(連想配列)で帰ってくるので PHP の MDB を使っていた頃とあまり使い勝手が変わらない感じでしたが、Declarative API だとちゃんとオブジェクトで帰ってくるのでとても良いです。

ツイッターでシェア
みんなに共有、忘れないようにメモ

ともたこ

A programmer, desktop and web application developer, server engineer, and wannabe of indie game developer. プログラマ・デスクトップ/Webアプリ開発者・サーバエンジニア、そして個人でゲーム開発したい人です。

Crieitは誰でも投稿できるサービスです。 是非記事の投稿をお願いします。どんな軽い内容でも投稿できます。

また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!

有料記事を販売できるようになりました!

こじんまりと作業ログやメモ、進捗を書き残しておきたい方はボード機能をご利用ください。
ボードとは?

コメント