使用Diesel框架时,关联三张表(多对多关系)的标准模式是什么?

16

数据库 - Postgres

我有以下关系:

users <—>> users_organizations <<—> organizations

架构:

table! {
    organizations (id) {
        id -> Int4,
        name -> Varchar,
    }
}

table! {
    users (id) {
        id -> Int4,
        name -> Varchar,
        email -> Varchar,
        password -> Varchar,
    }
}

table! {
    users_organizations (id, user_id, organization_id) {
        id -> Int4,
        user_id -> Int4,
        organization_id -> Int4,
    }
}

模型:

#[derive(Identifiable, Queryable, Debug, Serialize, Deserialize)]
pub struct Organization {
    pub id: i32,
    pub name: String,
}


#[derive(Identifiable, Queryable, PartialEq, Debug, Serialize, Deserialize)]
pub struct User {
    pub id: i32,
    pub name: String,
    pub email: String,
    pub password: String,
}


#[derive(Identifiable, Queryable, Debug, Associations, Serialize, Deserialize)]
#[belongs_to(User)]
#[belongs_to(Organization)]
#[table_name = "users_organizations"]
pub struct UserOrganization {
    pub id: i32,
    pub user_id: i32,
    pub organization_id: i32,
}

我想创建一个组织。为了支持这种关系,我必须手动将用户和组织的ID添加到users_organizations表中。有没有更好的方法来实现这种关系?

let new_organization = NewOrganization { name: &msg.name };
let organization = insert_into(organizations::table)
    .values(&new_organization)
    .get_result::(conn)
    .map_err(|_| error::ErrorInternalServerError(“Error creating organization”))?;

let new_user_org = NewUserOrganizationIDs {
    user_id: msg.user_id,
    organization_id: organization.id,
};

insert_into(users_organizations::table)
    .values(&new_user_org)
    .get_result::<UserOrganization>(conn)
    .map_err(|_| error::ErrorInternalServerError("Error creating user-organization data"))

我也有同样的问题。如果选择所有与用户相关的组织(反之亦然),我想到了以下代码:

let user = users::table.filter(users::id.eq(&msg.user_id))
        .get_result::<User>(conn)
        .map_err(|_| error::ErrorNotFound("User doesn't exist"))?;

let user_organizations = UserOrganization::belonging_to(&user)
    .get_results::<UserOrganization>(conn)
    .map_err(|_| error::ErrorNotFound("User doesn't have any organization"))?;

let mut organization_ids = vec![];
for user_org in &user_organizations {
    organization_ids.push(user_org.organization_id);    
}

organizations::table.filter(organizations::id.eq_any(organization_ids))
    .get_results::<Organization>(conn)
    .map_err(|_| error::ErrorNotFound("Organizations don't exist"))

1
我今天也遇到了同样的问题,但仍然没有找到任何解决方案。我唯一能建议你的是考虑将所有操作放在一个事务中运行,这样如果一个插入失败,你就不会有不一致的数据。 - vasilakisfil
1个回答

22

这个答案来自Diesel聊天室的@SRugina和@weiznich(已编辑并适应问题)。

我如何使用Diesel编写多对多关系?

通常我会组合belonging_tojoin,就像这样:

UserOrganization::belonging_to(&organizations)
    .inner_join(user::table)

有类似于belonging_to_many的东西吗?

不,belonging_to_many不存在,因为Diesel不会尝试隐藏数据库。这样做会在您想要执行复杂或非标准操作时引起问题。根据您的确切用例,连接所有三个表也可以是一种选择。

我该如何在这种情况下使用inner_join

你有三张表:usersorganizationsuser_organizations,你想要获取一个特定用户的所有组织。

有两种变体。第一种变体只需要一次查询,但如果您想要针对所有用户执行此操作,则可能无法匹配您需要的数据布局:

users::table
    .inner_join(user_organizations::table.inner_join(organizations::table))
    .filter(users::id.eq(user_id))
    .select(organizations::all_columns)
    .load::<Organization>(conn)?;
第二种变体允许使用内置的关联 API 将结果按每个用户分组:
let user = users::table
    .find(user_id)
    .first::<User>(conn)?;

UserOrganization::belonging_to(&user)
    .inner_join(organizations::table)
    .select(organizations::all_columns)
    .load::<Organization>(conn)?;

插入需要三个单独的插入。我们不会试图隐藏这一点,因为最终用户可以自行选择如何处理插入失败时的数据一致性问题。使用事务是一个常见的选择。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接