#python #sqlalchemy #relationship
Вопрос:
В моей базе данных есть две таблицы: одна для матчей и одна для команд. В матче есть столбцы, в которых хранятся идентификаторы играющих команд.
class Match(Base):
id_ = Column(UUID(as_uuid=True, primary_key=True)
team1_id = Column(UUID(as_uuid=True), ForeignKey('teams.id')
team2_id = Column(UUID(as_uuid=True), ForeignKey('teams.id')
class Team(Base):
id_ = Column(UUID(as_uuid=True, primary_key=True)
Я хотел бы создать взаимосвязь между двумя таблицами таким образом, чтобы:
Match.teams
возвращает список из двухTeam
объектов, содержащий команды с идентификаторами, совпадающимиteam1_id
илиteam2_id
Team.matches
возвращает списокMatch
объектов, содержащий все совпадения, в которых идентификатор этой команды присутствует вteam1_id
team2_id
столбцах или
С точки зрения SQL мне не нужна другая таблица — вся информация о взаимоотношениях присутствует в таблицах, как указано выше. Однако требуется ли SQLAlchemy таблица ассоциаций для этой работы?
Попытка 1:
class Match(Base):
...
teams = relationship('Team', back_populates='matches', lazy='selectin',
foreign_keys=[team1_id, team2_id])
class Team(Base):
...
matches = relationship('Match', back_populates='teams', lazy='selectin')
В результате возникла следующая ошибка:
sqlalchemy.exc.AmbiguousForeignKeysError: Could not determine join condition between parent/child tables on relationship Match.teams - there are multiple foreign key paths linking the tables.
Specify the 'foreign_keys' argument, providing a list of those columns which should be counted as containing a foreign key reference to the parent table.
Что смутило меня, когда я подумал, что именно это я и делал! Что я сделал не так?
Попытка 2:
class Match(Base):
...
teams = relationship('Team',
back_populates='matches',
lazy='selectin',
primaryjoin=('or_(Team.id==Match.team1_id,'
'Team.id==Match.team2_id)'))
class Team(Base):
...
matches = relationship('Match', back_populates='teams', lazy='selectin')
Это привело к Match.teams
возвращению одного значения первой команды с идентификатором, соответствующим team1_id
или team2_id
.
Возможно ли то, что я хочу сделать? Как это должно быть сделано? Можно ли обойтись без создания еще одной таблицы в базе данных?
Ответ №1:
Для этого можно использовать свойство, которое может быть легче прочитать:
class Match(Base):
...
@property
def teams(self) -> Tuple[Team, Team]:
return session.get(Team, self.team1_id), session.get(Team, self.team2_id)
Вы бы использовали его так же, как и любой другой атрибут: match.teams
это кортеж командных объектов.
Комментарии:
1. Учитывая, что это вариант, почему
relationship
существуют sqlalchemy s? В чем видимое преимущество?2. Обратите внимание, что это не будет работать с асинхронными базами данных
3. Это
relationship
просто и легко для обычного случая: это поле относится к этому полю. Он дает вам описание на английском языке, и SQLAlchemy выполняет запросы за вас. Пример в вопросе, безусловно, необычный случай, поэтому я не удивлен, что он не поддерживается из коробки.