Какова стандартная схема для связи трех таблиц (отношение "многие ко многим") в дизеле?
База данных - 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,
}
Я хочу создать организацию. Чтобы поддерживать такое отношение, мне нужно вручную добавить идентификаторы пользователя и организации в таблицу 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"))
0 ответов
Этот ответ взят из чата Diesel от @SRugina и @weiznich (отредактировано и адаптировано для вопроса).
Как мне написать отношения "многие ко многим" с Дизелем?
Я обычно совмещаю 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)?;
Для вставки требуются три отдельных вставки. Мы не пытаемся скрыть это, потому что, в конце концов, это выбор пользователя, как обеспечить согласованность данных в случае неудачной вставки. Использование транзакции - это распространенный выбор.