Многие-ко-многим SQLAlchemy
Есть две таблицы с предопредленными именами имя_класса.lower()+'s' и id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
Не получается создать связь многие-ко-многим между 2 таблицами и между записями первой таблицы.
Таблица "сборки":
class Assembly(Base):
serial_number: Mapped[str_uniq] = mapped_column(String)
name: Mapped[str] = mapped_column(String)
link: Mapped[str] = mapped_column(String)
deleted_at: Mapped[DateTime] = mapped_column(DateTime)
# Связь с таблицей assemblyparts через backref
parts: Mapped[list['Part']] = relationship(
'Part',
secondary='assemblyparts',
back_populates='assemblys'
)
# Связь с другими сборками через backref
child_assemblys: Mapped[list['Assembly']] = relationship(
'Assembly',
secondary='assemblycompositions',
primaryjoin=id == column('parent_assembly_id'),
secondaryjoin=id == column('child_assembly_id'),
back_populates='parent_assemblys'
)
parent_assemblys: Mapped[list['Assembly']] = relationship(
'Assembly',
secondary='assemblycompositions',
primaryjoin=id == column('child_assembly_id'),
secondaryjoin=id == column('parent_assembly_id'),
back_populates='child_assemblys'
)
и "Детали":
class Part(Base):
serial_number: Mapped[str_uniq] = mapped_column(String)
name: Mapped[str] = mapped_column(String)
link: Mapped[str] = mapped_column(String)
deleted_at: Mapped[DateTime] = mapped_column(DateTime)
# Связь с таблицей assembly_parts через backref
assemblys: Mapped[list['Assembly']] = relationship(
'Assembly',
secondary='assemblyparts',
back_populates='parts'
)
А также таблицы ассоциации:
class AssemblyPart(Base):
'''Ассоциативная таблица для связей между сборками и деталями'''
assembly_id: Mapped[int] = mapped_column(ForeignKey('assemblys.id'), primary_key=True)
part_id: Mapped[int] = mapped_column(ForeignKey('parts.id'), primary_key=True)
quantity: Mapped[int] = mapped_column(Integer)
class AssemblyComposition(Base):
'''Промежуточная таблица для связи many-to-many между сборками'''
parent_assembly_id: Mapped[int] = mapped_column(ForeignKey('assemblys.id'))
child_assembly_id: Mapped[int] = mapped_column(ForeignKey('assemblys.id'))
quantity: Mapped[int] = mapped_column(Integer)
# Составной первичный ключ
__table_args__ = (
PrimaryKeyConstraint('parent_assembly_id', 'child_assembly_id'),
)
Для связи многие-ко-многим между Assembly и Part используется таблица AssemblyPart - каждая деталь может входить в несколько сборок, а каждая сборка может содержать несколько деталей. Кроме того, каждая сборка может включать в себя другие сборки и сама входить в другие сборки.
При инициализации таблиц появляется ошибка:
sqlalchemy.exc.ArgumentError: Could not locate any relevant foreign key columns for primary join condition 'parent_assembly_id = :parent_assembly_id_1' on relationship Assembly.child_assemblys.
Ensure that referencing columns are associated with a ForeignKey or ForeignKeyConstraint, or are annotated in the join condition with the foreign() annotation.
Если убрать ассоциативную таблицу для связи сборок между собой (AssemblyComposition) и удалить для нее параметры из Assembly , то инициализация таблиц срабатывает корректно. Т.е. связь между деталями и сборками определена верно.
Что я делаю не так?
Ответы (1 шт):
Пока не создашь топик - не получится.
В общем помогло явное указание в параметрах primaryjoin и secondaryjoin -какие поля в Assembly используются для связи:
# Связь с другими сборками через backref
child_assemblys: Mapped[list['Assembly']] = relationship(
'Assembly',
secondary='assemblycompositions',
primaryjoin="Assembly.id == AssemblyComposition.parent_assembly_id",
secondaryjoin="Assembly.id == AssemblyComposition.child_assembly_id",
back_populates='parent_assemblys'
)
parent_assemblys: Mapped[list['Assembly']] = relationship(
'Assembly',
secondary='assemblycompositions',
primaryjoin="Assembly.id == AssemblyComposition.child_assembly_id",
secondaryjoin="Assembly.id == AssemblyComposition.parent_assembly_id",
back_populates='child_assemblys'
)