如何用SQLAlchemy正确实现用户多好友关系?代码写法求评估
正确实现SQLAlchemy中「用户多好友」关联关系的方案
先直接指出你现有代码的核心问题:
friends关联的是UserFriend中间表对象,但实际需求应该是关联到User对象,这样才能直接获取好友的完整用户信息,而非中间表的记录。- 中间表
UserFriend设置了自增主键id,对于无额外字段的好友关联场景,这个主键完全冗余,改用user_id+friend_id的联合主键更合理,既能避免重复的好友关系,又节省存储资源。
下面分两种场景给出最优实现:
场景一:仅需存储好友关系(无额外字段)
这种是最常见的情况,推荐使用仅存双ID的联合主键中间表,实现直接关联用户对象:
from sqlalchemy import Table, Column, Integer, ForeignKey, String from sqlalchemy.orm import Mapped, mapped_column, relationship from sqlalchemy.ext.declarative import declarative_base Base = declarative_base() # 定义无实体类的中间表,仅存关联ID user_friends = Table( 'user_friends', Base.metadata, Column('user_id', Integer, ForeignKey('users.id'), primary_key=True), Column('friend_id', Integer, ForeignKey('users.id'), primary_key=True) ) class User(Base): __tablename__ = 'users' id: Mapped[int] = mapped_column(Integer, primary_key=True) name: Mapped[str] = mapped_column(String(255), nullable=False) balance: Mapped[float] = mapped_column(Integer, nullable=False) # 正向关联:当前用户的好友列表 friends: Mapped[list["User"]] = relationship( "User", secondary=user_friends, primaryjoin=(user_friends.c.user_id == id), secondaryjoin=(user_friends.c.friend_id == id), back_populates="friends_of" ) # 反向关联:把当前用户加为好友的人 friends_of: Mapped[list["User"]] = relationship( "User", secondary=user_friends, primaryjoin=(user_friends.c.friend_id == id), secondaryjoin=(user_friends.c.user_id == id), back_populates="friends" )
该方案的优势
- 数据冗余最少:仅存储必要的关联ID,联合主键天然避免重复的好友关系。
- 查询效率更高:直接通过
user.friends获取好友的完整用户对象,无需额外关联中间表。 - 维护成本低:结构简单,无需处理中间表的冗余字段。
场景二:需要存储好友关系的额外信息
如果你的好友关系需要记录添加时间、备注、拉黑状态等额外字段,就需要自定义中间表实体类,调整后的代码如下:
from sqlalchemy import Integer, ForeignKey, String, DateTime from sqlalchemy.orm import Mapped, mapped_column, relationship from sqlalchemy.ext.declarative import declarative_base from datetime import datetime Base = declarative_base() class User(Base): __tablename__ = 'users' id: Mapped[int] = mapped_column(Integer, primary_key=True) name: Mapped[str] = mapped_column(String(255), nullable=False) balance: Mapped[float] = mapped_column(Integer, nullable=False) # 关联到中间表实体,用于操作额外字段 friend_relations: Mapped[list["UserFriend"]] = relationship(back_populates="user") # 只读关联到好友用户对象,方便直接获取好友信息 friends: Mapped[list["User"]] = relationship( "User", secondary="user_friends", primaryjoin=(id == UserFriend.user_id), secondaryjoin=(id == UserFriend.friend_id), viewonly=True ) class UserFriend(Base): __tablename__ = 'user_friends' # 用联合主键替代自增ID,避免重复好友关系 user_id: Mapped[int] = mapped_column(ForeignKey("users.id"), primary_key=True) friend_id: Mapped[int] = mapped_column(ForeignKey("users.id"), primary_key=True) # 额外字段示例 add_time: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow) remark: Mapped[str] = mapped_column(String(255), nullable=True) user: Mapped["User"] = relationship(back_populates="friend_relations") friend: Mapped["User"] = relationship()
调整说明
- 移除中间表的自增
id,改用user_id+friend_id作为联合主键,确保同一好友关系不会重复存储。 - 给
User类设置两个关系:friend_relations用于操作中间表的额外字段,friends用于只读获取好友的用户对象。
内容的提问来源于stack exchange,提问作者endingofourlife
相关产品推荐
相关产品推荐

