SQLALCHEMY/FASTAPI/POSTGRESQL |只检索双重条目

SQLALCHEMY/FASTAPI/POSTGRESQL | Only retrieve double entries

我有一个名为 friends 的 table 数据库。 table 有两列,"user_id" and "friend_id"。 这些是来自 Users table.

的外键

我的朋友 table 现在:

               user_id               |              friend_id
-------------------------------------+-------------------------------------
 google-oauth2|11539665289********** | google-oauth2|11746442253**********
 google-oauth2|11746442253********** | google-oauth2|11539665289**********
 google-oauth2|11746442253********** | google-oauth2|11111111111**********

前两行是相同的ID,但翻转了。那些 Users 我想检索,因为他们添加了彼此。第三行只加了一个人,那个不应该被找回来

我的 SQLModels (models.py):

class Friends(SQLModel, table=True):
    __tablename__ = "friends"
    user_id: str = Field(sa_column=Column('user_id', VARCHAR(length=50), primary_key=True), foreign_key="users.id")
    friend_id: str = Field(sa_column=Column('friend_id', VARCHAR(length=50), primary_key=True), foreign_key="users.id")

class UserBase(SQLModel):
    id: str
    username: Optional[str]
    country_code: Optional[str]
    phone: Optional[str]
    picture: Optional[str]

    class Config:
        allow_population_by_field_name = True


class User(UserBase, table=True):
    __tablename__ = 'users'
    id: str = Field(primary_key=True)
    username: Optional[str] = Field(sa_column=Column('username', VARCHAR(length=50), unique=True, default=None))
    phone: Optional[str] = Field(sa_column=Column('phone', VARCHAR(length=20), unique=True, default=None))
    picture: Optional[str] = Field(sa_column=Column('picture', VARCHAR(length=255), default=None))

我的 fastapi 端点:

@router.get("", status_code=status.HTTP_200_OK, response_model=models.FriendsList, name="Get Friends for ID",
            tags=["friends"])
async def get_friends(
        user_id: str = Query(default=None, description="The user_id that you want to retrieve friends for"),
        session: Session = Depends(get_session)
):
    stm = select(models.User, models.Friends).where(models.User.id == models.Friends.friend_id, models.Friends.user_id == user_id)
    res = session.exec(stm).all()
    if not res:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND,
                            detail="There are no friendships associated with this id.")
    users = []
    for item in res:
        users.append(item[0])
    return models.FriendsList(users=users)

我的代码工作得很好,只有查询需要被替换。

stm = select(models.User, models.Friends).where(models.User.id == models.Friends.friend_id, models.Friends.user_id == user_id)
res = session.exec(stm).all()

此查询 returns 每个 User 给定 ID 为 user_id,但不检查是否有相反的条目。

我想要得到的示例:

我向 ID 为 google-oauth2|11746442253********** 的端点发出 GET 请求。我会得到用户 google-oauth2|11539665289**********。 (用户 google-oauth2|11111111111********** 不会被检索到,因为没有相反的条目)

希望你们理解我的问题。如有任何问题,请随时提出。

此致, 科林

正如我在评论中所说,没有一个简单的例子我无法真正尝试自己,但我确实有一个想法。您可能需要稍微修改一下子查询语法,但我认为理论上这可行:

stmt = select(Friends.user_id, Friends.friend_id).where(
    tuple_(Friends.user_id, Friends.friend_id).in_(select(Friends.friend_id, Friends.user_id)) 
)

基本上它只是检查每个 (user_id, friend_id) 是否有匹配的 (friend_id, user_id)

你能添加另一个名为“已接受”的列,其值为 0 或 1 吗?

           user_id                   |          friend_id                  | accepted
-------------------------------------+-----------------------------------------------
 google-oauth2|11539665289********** | google-oauth2|11746442253********** | 1
 google-oauth2|11746442253********** | google-oauth2|11539665289********** | 1
 google-oauth2|11746442253********** | google-oauth2|11111111111********** | 0

那么你有两个选择:

  1. 你可以在用户 table 上建立一个名为“朋友”的关系,并将惰性参数设置为“动态”(lazy='dynamic'),然后查询:user.friends.filter_by(accepted=1).all()

  2. 或者您可以编写如下查询:

    query = Friends.query.filter(Friends.user_id==user_id).filter(Friends.accepted == 1).all()

一般而言,关系数据库不是这些类型场景的最佳解决方案 - 如果您非常灵活并且不太深入,您可以检查像 MongoDB

这样的 NoSQL 解决方案