SQLAlchemy 使用 m:n 关系链接表

问题描述 投票:0回答:1

我正在尝试为我的数据库创建 orm 类。 我有一个表歌曲、一个表艺术家和一个表 Song_artist 来修复 m:n 关系。

class Song(Base):
    __tablename__ = 'songs'

    SONG_ID: Mapped[int] = mapped_column(Integer, primary_key=True, nullable=False, index=True)
    ALBUM_ID: Mapped[int] = mapped_column(Integer, ForeignKey('albums.ALBUM_ID'), nullable=False)
    GENRE_ID: Mapped[int] = mapped_column(Integer, ForeignKey('genres.GENRE_ID'), nullable=False)
    FILE_ID: Mapped[int] = mapped_column(Integer, ForeignKey('files.FILE_ID'), nullable=False)
    DURATION: Mapped[int] = mapped_column(Integer)
    TITLE: Mapped[str] = mapped_column(String(100))
    RELEASE_DATE: Mapped[Date] = mapped_column(Date)

    file: Mapped['File'] = relationship(back_populates="song")
    album: Mapped['Album'] = relationship(back_populates="song")
    genre: Mapped['Genre'] = relationship(back_populates="song")
    artist: Mapped[List['Artist']] = relationship(secondary="songs_artists", back_populates="song"

class Artist(Base):
    __tablename__ = 'artists'

    ARTIST_ID: Mapped[int] = mapped_column(Integer, primary_key=True)
    ARTIST_NAME: Mapped[str] = mapped_column(String(100))

    song: Mapped['Song'] = relationship(secondary="songs_artists",back_populates="artist")

songs_artists = Table(
    'songs_artists',
    Base.metadata,
    Column('SONG_ID', Integer, ForeignKey('songs.SONG_ID'), primary_key=True),
    Column('ARTIST_ID', Integer, ForeignKey('artists.ARTIST_ID'), primary_key=True)
)

我收到以下错误消息:

sqlalchemy.exc.InvalidRequestError: When initializing mapper Mapper[Artist(artists)], expression 'songs_artists' failed to locate a name ("name 'songs_artists' is not defined"). If this is a class name, consider adding this relationship() to the <class 'src.database.musicDB.db_model_artist.Artist'> class after both dependent classes have been defined
.

我现在尝试更换桌子几个小时,但无法解决问题。

如有任何帮助,将不胜感激。 谢谢

python-3.x sqlalchemy fastapi
1个回答
0
投票
  1. 当您提出问题时,从代码中删除所有不相关的信息(如
    file
    album
    等字段)并包含导入和测试代码
  2. 将关联表(
    songs_artists
    )放在模型之前
  3. 由于它是多对多关系,因此在描述关系时使用单词“song”和“artis”的复数形式:
    artists: Mapped[List["Artist"]] = relationship(
        secondary="songs_artists", back_populates="songs"
    )
    songs: Mapped[List["Song"]] = relationship(
        secondary="songs_artists", back_populates="artists"
    )

工作示例:

from typing import List

from sqlalchemy import Column, ForeignKey, Integer, String, Table, create_engine
from sqlalchemy.orm import (
    DeclarativeBase,
    Mapped,
    mapped_column,
    relationship,
    sessionmaker,
)

class Base(DeclarativeBase):
    pass

songs_artists = Table(
    "songs_artists",
    Base.metadata,
    Column("SONG_ID", Integer, ForeignKey("songs.SONG_ID"), primary_key=True),
    Column("ARTIST_ID", Integer, ForeignKey("artists.ARTIST_ID"), primary_key=True),
)

class Song(Base):
    __tablename__ = "songs"

    SONG_ID: Mapped[int] = mapped_column(
        Integer, primary_key=True, nullable=False, index=True
    )

    # ALBUM_ID: Mapped[int] = mapped_column(Integer, ForeignKey('albums.ALBUM_ID'), nullable=False)
    # GENRE_ID: Mapped[int] = mapped_column(Integer, ForeignKey('genres.GENRE_ID'), nullable=False)
    # FILE_ID: Mapped[int] = mapped_column(Integer, ForeignKey('files.FILE_ID'), nullable=False)
    # DURATION: Mapped[int] = mapped_column(Integer)
    TITLE: Mapped[str] = mapped_column(String(100))
    # RELEASE_DATE: Mapped[Date] = mapped_column(Date)

    # file: Mapped['File'] = relationship(back_populates="song")
    # album: Mapped['Album'] = relationship(back_populates="song")
    # genre: Mapped['Genre'] = relationship(back_populates="song")
    artists: Mapped[List["Artist"]] = relationship(
        secondary="songs_artists", back_populates="songs"
    )

class Artist(Base):
    __tablename__ = "artists"

    ARTIST_ID: Mapped[int] = mapped_column(Integer, primary_key=True)
    ARTIST_NAME: Mapped[str] = mapped_column(String(100))

    songs: Mapped[List["Song"]] = relationship(
        secondary="songs_artists", back_populates="artists"
    )

engine = create_engine("sqlite://")
Base.metadata.create_all(bind=engine)

session_maker = sessionmaker(bind=engine)
with session_maker() as session:
    song_1 = Song(TITLE="s 1")
    song_2 = Song(TITLE="s 2")

    artist_1 = Artist(ARTIST_NAME="a 1", songs=[song_1])
    artist_2 = Artist(ARTIST_NAME="a 2", songs=[song_1, song_2])

    session.add_all((song_1, song_2, artist_1, artist_2))
    session.commit()
© www.soinside.com 2019 - 2024. All rights reserved.