SQLAlchemy Coreで多対多関係をマスターする: 詳細なチュートリアル
SQLAlchemy Coreにおける多対多関係の作成方法
このチュートリアルでは、SQLAlchemy Coreを使用して多対多関係を作成する方法を、分かりやすく日本語で解説します。
エンティティの定義
まず、関連するエンティティを定義する必要があります。ここでは、書籍と著者という2つのエンティティを例とします。
from sqlalchemy import Column, Integer, String, Table, MetaData
from sqlalchemy.ext.declarative import declarative_base
metadata = MetaData()
Base = declarative_base()
class Book(Base):
__tablename__ = 'books'
id = Column(Integer, primary_key=True)
title = Column(String(255), nullable=False)
class Author(Base):
__tablename__ = 'authors'
id = Column(Integer, primary_key=True)
name = Column(String(255), nullable=False)
多対多関係の定義
次に、書籍と著者間の多対多関係を定義する必要があります。そのためには、中間テーブルを作成し、両方のエンティティとの参照関係を定義します。
book_author_association_table = Table(
'book_author_association',
metadata,
Column('book_id', Integer, ForeignKey('books.id')),
Column('author_id', Integer, ForeignKey('authors.id'))
)
エンティティ間の関係の定義
それぞれのエンティティクラスに、多対多関係を表す属性を追加します。
class Book(Base):
# ...
authors = relationship(
"Author",
secondary=book_author_association_table,
backref="books"
)
class Author(Base):
# ...
books = relationship(
"Book",
secondary=book_author_association_table,
backref="authors"
)
データベースへの保存
エンティティインスタンスを作成し、データベースに保存することで、多対多関係が確立されます。
book = Book(title="SQLAlchemy入門")
author1 = Author(name="山田太郎")
author2 = Author(name="佐藤花子")
book.authors.append(author1)
book.authors.append(author2)
session.add(book)
session.commit()
secondary
オプションは、中間テーブルを指定するために使用されます。backref
オプションは、逆方向の関係を定義するために使用されます。
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, Table, MetaData
# データベース接続
engine = create_engine("sqlite:///database.db")
# メタデータ
metadata = MetaData()
# ベースクラス
Base = declarative_base()
# エンティティ定義
class Book(Base):
__tablename__ = 'books'
id = Column(Integer, primary_key=True)
title = Column(String(255), nullable=False)
class Author(Base):
__tablename__ = 'authors'
id = Column(Integer, primary_key=True)
name = Column(String(255), nullable=False)
# 中間テーブル定義
book_author_association_table = Table(
'book_author_association',
metadata,
Column('book_id', Integer, ForeignKey('books.id')),
Column('author_id', Integer, ForeignKey('authors.id'))
)
# エンティティ間の関係定義
class Book(Base):
# ...
authors = relationship(
"Author",
secondary=book_author_association_table,
backref="books"
)
class Author(Base):
# ...
books = relationship(
"Book",
secondary=book_author_association_table,
backref="authors"
)
# テーブル作成
Base.metadata.create_all(engine)
# データ操作
session = create_session(bind=engine)
# エンティティインスタンス作成
book = Book(title="SQLAlchemy入門")
author1 = Author(name="山田太郎")
author2 = Author(name="佐藤花子")
# 多対多関係の構築
book.authors.append(author1)
book.authors.append(author2)
# データベースへの保存
session.add(book)
session.commit()
# データ取得
book = session.query(Book).get(1)
for author in book.authors:
print(author.name)
# データベースクリーンアップ
session.close()
engine.dispose()
説明
- データベース接続:
create_engine()
を使用して、SQLiteデータベースへの接続を作成します。 - メタデータ:
MetaData()
を使用して、テーブルと列に関する情報を格納するメタデータオブジェクトを作成します。 - ベースクラス:
declarative_base()
を使用して、エンティティクラスのベースとなるクラスを作成します。 - エンティティ定義:
Book
とAuthor
という2つのエンティティクラスを定義します。各クラスには、id
、title
、name
などの属性があります。 - エンティティ間の関係定義:
Book
とAuthor
クラスのそれぞれに、authors
とbooks
という属性を追加します。これらの属性は、多対多関係を表す関係オブジェクトです。 - テーブル作成:
Base.metadata.create_all(engine)
を使用して、定義したテーブルをデータベースに作成します。 - データ操作:
create_session()
を使用して、データベース操作を行うためのセッションを作成します。 - エンティティインスタンス作成:
Book
とAuthor
のエンティティインスタンスを作成します。 - 多対多関係の構築:
book.authors.append(author1)
とbook.authors.append(author2)
を使用して、書籍と著者間の多対多関係を構築します。 - データベースへの保存:
session.add(book)
を使用して、エンティティインスタンスをデータベースに追加します。session.commit()
を使用して、変更をコミットします。 - データ取得:
session.query(Book).get(1)
を使用して、IDが1の書籍を取得します。for author in book.authors:
を使用して、書籍に関連するすべての著者をループ処理します。 - データベースクリーンアップ:
session.close()
を使用して、セッションを閉じます。engine.dispose()
を使用して、データベース接続を破棄します。
class Book(Base):
# ...
authors = relationship(
"Author",
secondary="books_authors",
primaryjoin=(book_author_association_table.c.book_id == id),
secondaryjoin=(book_author_association_table.c.author_id == Author.id),
backref="books"
)
class Author(Base):
# ...
books = relationship(
"Book",
secondary="books_authors",
primaryjoin=(book_author_association_table.c.author_id == id),
secondaryjoin=(book_author_association_table.c.book_id == Book.id),
backref="authors"
)
このコードでは、secondary
オプションにbooks_authors
という文字列を指定しています。これは、中間テーブルの名前を表します。また、primaryjoin
とsecondaryjoin
オプションを使用して、中間テーブルの結合条件を指定しています。
外部キー制約の利用
中間テーブルを定義せずに、外部キー制約を使用して多対多関係を作成することもできます。
class Book(Base):
# ...
authors = relationship(
"Author",
secondary=book_author_association_table,
backref="books",
lazy="dynamic"
)
class Author(Base):
# ...
books = relationship(
"Book",
secondary=book_author_association_table,
backref="authors",
lazy="dynamic"
)
このコードでは、secondary
オプションに中間テーブルオブジェクトではなく、book_author_association_table
という文字列を指定しています。これは、中間テーブルの名前を表します。また、lazy
オプションを使用して、関係の読み込みを遅延しています。
カスタム関係クラスの作成
より複雑な関係を定義したい場合は、カスタム関係クラスを作成することができます。
from sqlalchemy.orm import relationship
from sqlalchemy.ext.hybrid import hybrid_property
class BookAuthorAssociation(Base):
__tablename__ = 'book_author_association'
book_id = Column(Integer, ForeignKey('books.id'))
author_id = Column(Integer, ForeignKey('authors.id'))
class Book(Base):
# ...
authors = relationship(
"Author",
secondary=BookAuthorAssociation,
backref="books",
lazy="dynamic"
)
class Author(Base):
# ...
books = relationship(
"Book",
secondary=BookAuthorAssociation,
backref="authors",
lazy="dynamic"
)
多対多関係を削除するには、以下の方法があります。
relationship.remove()
メソッドを使用する:
book = session.query(Book).get(1)
author = session.query(Author).get(2)
book.authors.remove(author)
session.commit()
book = session.query(Book).get(1)
book.authors.clear()
session.commit()
多対多関係のフィルタリング
多対多関係をフィルタリングするには、relationship.filter()
メソッドを使用します。
book = session.query(Book).get(1)
authors = book.authors.filter(Author.name.like("%山田%"))
for author in authors:
print(author.name)
sqlalchemy