Как уже упоминалось в комментарии, я предпочитаю расширенную модель, в которой Friendship
- это отдельная сущность, а связи между друзьями - это отдельные сущности. Таким образом, можно хранить как симметричные, так и асимметричные свойства (например, что один человек думает о другом). Таким образом, модель ниже должна показать вам, что я имею в виду:
...
class User(Base):
__tablename__ = "user"
id = Column(Integer, primary_key=True)
name = Column(String(255), nullable=False)
# relationships
friends = relationship('UserFriend', backref='user',
# ensure that deletes are propagated
cascade='save-update, merge, delete',
)
class Friendship(Base):
__tablename__ = "friendship"
id = Column(Integer, primary_key=True)
# additional info symmetrical (common for both sides)
status = Column(String(255), nullable=False)
# @note: also could store a link to a Friend who requested a friendship
# relationships
parties = relationship('UserFriend',
back_populates='friendship',
# ensure that deletes are propagated both ways
cascade='save-update, merge, delete',
)
class UserFriend(Base):
__tablename__ = "user_friend"
id = Column(Integer, primary_key=True)
friendship_id = Column(Integer, ForeignKey(Friendship.id), nullable=False)
user_id = Column(Integer, ForeignKey(User.id), nullable=False)
# additional info assymmetrical (different for each side)
comment = Column(String(255), nullable=False)
# @note: one could also add 1-N relationship where one user might store
# many different notes and comments for another user (a friend)
# ...
# relationships
friendship = relationship(Friendship,
back_populates='parties',
# ensure that deletes are propagated both ways
cascade='save-update, merge, delete',
)
@property
def other_party(self):
return (self.friendship.parties[0]
if self.friendship.parties[0] != self else
self.friendship.parties[1]
)
def add_friend(self, other_user, status, comment1, comment2):
add_friendship(status, self, comment1, other_user, comment2)
# helper method to add a friendship
def add_friendship(status, usr1, comment1, usr2, comment2):
""" Adds new link to a session. """
pl = Friendship(status=status)
pl.parties.append(UserFriend(user=usr1, comment=comment1))
pl.parties.append(UserFriend(user=usr2, comment=comment2))
return pl
Таким образом, добавив дружба довольно легко.
Так же обновляет любые его атрибуты. Вы можете создать больше вспомогательных методов, таких как add_friend
.
При указанной выше конфигурации cascade
удаление a User or Friendship or UserFriend
обеспечит удаление обеих сторон.
Выбор всех друзей так прост, как вы хотите: print user.friends
Реальная проблема с этим решением состоит в том, чтобы гарантировать, что есть точно 2 UserFriend
ссылки для каждого Friendship
. Опять же, при манипулировании объектами из кода это не должно быть проблемой, но база данных потенциально может быть несовместимой, если кто-то импортирует / манипулирует некоторыми данными непосредственно на стороне SQL.