在 Diesel 中关联三个表(多对多关系)的标准模式是啥?
Posted
技术标签:
【中文标题】在 Diesel 中关联三个表(多对多关系)的标准模式是啥?【英文标题】:What is the standard pattern to relate three tables (many-to-many relation) within Diesel?在 Diesel 中关联三个表(多对多关系)的标准模式是什么? 【发布时间】:2019-02-16 04:08:48 【问题描述】:数据库 - 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】:此答案来自@SRugina 和@weiznich 的the Diesel chat(针对问题进行了编辑和改编)。
如何编写与 Diesel 的多对多关系?
我通常将belonging_to
和join
组合在一起,所以类似于:
UserOrganization::belonging_to(&organizations)
.inner_join(user::table)
有类似belonging_to_many
的东西吗?
不,belonging_to_many
不存在,因为 Diesel 不会尝试对您隐藏数据库。一旦您想做复杂或非标准的事情,这样做就会引起问题。根据您的具体用例,也可以选择加入所有三个表。
在这种情况下如何使用inner_join
?
您有三个表:users
、organizations
和 user_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)?;
插入需要三个单独的插入。我们不会试图隐藏这一点,因为最终在插入失败的情况下如何处理数据一致性是用户的选择。使用事务是一种常见的选择。
【讨论】:
以上是关于在 Diesel 中关联三个表(多对多关系)的标准模式是啥?的主要内容,如果未能解决你的问题,请参考以下文章
关于多对多关系表做一个级联更新的问题(MYSQL),求高手解答SQL语句