У меня есть проект с этими моделями и отношениями:
- Игрок
- Команда
- Игры
- Тренер
- Доктор
- Медицинская специальность
- Спонсор
- Команда
Это, конечно, пример, но, как видите, я могу иметь на столе Player а team_id столбец как FK (внешний ключ) для Team который имеет coach_id столбец как FK для Coach стол.
В моем бэкэнде Golang я использую ORM который позволяет вам (генерировать и) использовать такой код:
type Player struct {
id string
name string
Team *Team
//...
}
type Team struct {
id string
Coach *Coach
//...
}
type Coach struct {
id string
//...
}
func (repo *Repo) PlayerById(id string) DomainPlayer {
player := repo.queryById(id).withTeam().withCoach()
// player here has TEAM and TEAM.COACH using a unique SQL query
// and I can easily convert it:
return ConvertPlayerToDomainPlayer(player)
}
Начиная с Rust, я был очарован МореОРМ но теперь, когда я пытаюсь создать что-то более сложное, я понял, что не могу делать вещи, как в Go.
Я создание объектов с использованием sea-orm-cli из моей базы данных Postgresql, созданной с использованием Мигратор SeaORM (это совершенно необязательно, вы также можете написать следующий код самостоятельно).
Сгенерированные объекты выглядят так:
//! SeaORM Entity. Generated by sea-orm-codegen 0.9.3
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
#[sea_orm(table_name = "player")]
pub struct Model {
#[sea_orm(primary_key, unique)]
pub id: String,
pub name: String,
pub team_id: Option<String>,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
#[sea_orm(
belongs_to = "super::team::Entity",
from = "Column::TeamId",
to = "super::team::Column::Id",
on_update = "NoAction",
on_delete = "NoAction"
)]
Team,
}
impl Related<super::team::Entity> for Entity {
fn to() -> RelationDef {
Relation::Team.def()
}
}
impl ActiveModelBehavior for ActiveModel {}
И я использую такой код:
async fn player_by_id(&self, id: String) -> Result<Option<DomainPlayer>> {
let player = match Player::find_by_id(id)
.find_also_related(Team)
// I would like to use here something like `with_team()` and `with_coach()` or `with_team(|team| team.with_coach())`
.one(self.db)
.await?
{
Some(o) => o,
None => return Ok(None),
};
let coach = if let Some(team) = &player.1 {
Coach::find_by_id(team.coach_id).one(self.db).await?
} else {
None
};
// Since I need to return a DomainPlayer (not a Player) here I cannot use a `Player.into()` because generated `Player` does not have `Team` field
Ok(Some(custom_func_to_transform_player_to_domain_player(player, player.1 /*this is team*/, coach)))
// Do you can imagine how complex is to use these functions?
}
Я хотел бы знать, что вы думаете об этом мысленном приказе.
Скорее всего, мне нужно изменить его, потому что код действительно уродлив (а также дорог для многих SQL-запросов и распределений) и неудобен для использования для масштабирования с еще большей сложностью.
Что вы порекомендуете?
![Вопросы о более сложных сценариях [closed] TheFAQ.ru](https://thefaq.ru/wp-content/uploads/2023/01/logo-250.png)