SQLAlchemyで1つのテーブルに複数種のリレーションを貼る
ある1つのテーブルに複数種のリレーションを貼りたい。
前提
「映画」と「言語」を対象に考えてみると、ある映画と言語には2つの関係が持たせられる:
- その映画がどの言語を対象にしたものか
- その映画は元々どの言語のものなのか
例えば日本語吹き替え版「バックトゥザフューチャー」は、対象言語が日本語で、 元言語が英語ということになる。
これについてのテーブルを考えて、FilmとLanguageという2つを用意する。 Filmには対象言語へのFKであるlanguage_idと、元言語へのFKであるoriginal_language_id の2つを持たせてやる。もちろんFKの対象はLanguageテーブルである。
こういったテーブルがある場合に SQLAlchemy でどうやって対応するのかという話。
ちなみに議題にあげたテーブルはMySQLの Sakila という サンプル用データベースの、Film、Languageテーブルのこと。
バージョン
- SQLAlchemy==0.8.0
書く
とりあえずこんなかんじ。nameとかのカラムは下には転記してない。
mport sqlalchemy as sa
from sqlalchemy.dialects import mysql
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
Base = declarative_base()
class Film(Base):
__tablename__ = 'film'
film_id = sa.Column(sa.SmallInteger(unsigned=True),
primary_key=True, nullable=False)
language_id = sa.Column(mysql.TINYINT(unsigned=True),
sa.ForeignKey("language.language_id"),
nullable=False)
original_language_id = sa.Column(mysql.TINYINT(unsigned=True),
sa.ForeignKey("language.language_id"))
language = relationship(
'Language',
primaryjoin="Film.language_id==Language.language_id")
original_language = relationship(
'Language',
primaryjoin="Film.original_language_id==Language.language_id")
class Language(Base):
__tablename__ = 'language'
language_id = sa.Column(mysql.TINYINT(unsigned=True),
nullable=False,
primary_key=True)
relationshipしてあげるわけですが、ここでprimaryjoinを指定してあげないと いけなかった。そうしないとlanguage_idとoriginal_language_idのどっちを使って Languageをとってくればいいか分からない。 language、original_languageの2つを用意してあげて、それぞれどういう条件で取ってくる かをprimaryjoinで教えてあげる。
あるけみすとへの道は遠い