SeaORM 操作多对多关系

260512
2022/06/01 01:53:54

本章将讨论多对多:一篇文章可以有多个标签,同样的,一个标签可以对应多篇文章。

多对多关系,在数据库中需要借助第三张表来维护。

数据表及示例数据

我们需要创建两个表,并插入一些示例数据:

说明
tags标签
article_tags文章和标签的对应关系

实体

多对多中间实体的定义

use sea_orm::entity::prelude::*;
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, PartialEq, DeriveEntityModel, Serialize, Deserialize)]
#[sea_orm(table_name = "article_tags")]
pub struct Model {
    #[sea_orm(primary_key)]
    #[serde(skip_deserializing)]
    pub id: i32,
    pub article_id: i32,
    pub tag_id: i32,
}

#[derive(Debug, Clone, Copy, EnumIter)]
pub enum Relation {
    Article,
    Tag,
}

impl RelationTrait for Relation {
    fn def(&self) -> sea_orm::RelationDef {
        match self {
            Self::Article => Entity::belongs_to(super::article::Entity)
                .from(Column::ArticleId)
                .to(super::article::Column::Id)
                .into(),
            Self::Tag => Entity::belongs_to(super::tag::Entity)
                .from(Column::TagId)
                .to(super::tag::Column::Id)
                .into(),
        }
    }
}

impl ActiveModelBehavior for ActiveModel {}

我们在中间实体中定义了两个关系:

  • Relation::Artice:通过 article_id = articles.id与文章建立关系
  • Relation:Tag:通过 tag_id = tags.id与标签建立关系

两个关系都是通过 belongs_to()定义的。

标签实体的定义

use sea_orm::entity::prelude::*;
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, PartialEq, DeriveEntityModel, Serialize, Deserialize)]
#[sea_orm(table_name = "tags")]
pub struct Model {
    #[sea_orm(primary_key)]
    #[serde(skip_deserializing)]
    pub id: i32,
    pub name: String,
}

#[derive(Debug, Clone, Copy, EnumIter)]
pub enum Relation {}

impl RelationTrait for Relation {
    fn def(&self) -> sea_orm::RelationDef {
        panic!()
    }
}

impl Related<super::article::Entity> for Entity {
    fn to()->RelationDef {
        super::article_tag::Relation::Article.def()
    }
    fn via() -> Option<RelationDef> {
        Some(super::article_tag::Relation::Tag.def().rev())
    }
}

impl ActiveModelBehavior for ActiveModel {}

我们并没有给标签实体定义关系,而是通过 Related<super::article::Entity>告诉 SeaORM如何去找所需要的文章实体。

Related::to()

Related::to():某个实体是否与另一个实体相关。

tag::Entity::Related<super::article::Entity>::to() 表示, tag::Entity里的article::Entity是通过 article_tag::Relation::Article.def()获得的。完整的路径是:Tag -> ArticleTag -> Article

Related::via()

Related::via():某个实体是否通过另一个实体关联。

tag::Entity::Related<super::article::Entity>::via()的结果是,原始的ArticleTag -> Tag 将变成 Tag -> ArticleTag

to()via() 我也不知道怎么描述,或许你的理解能力比我强,欢迎分享。理解不了也没关系,照抄就好了。——通过 SeaORM 生成的 SQL 语句来验证是否正确、是否合理。

文章实体中定义和标签的关系

src/entity/article.rs添加和标签的关系:

impl Related<super::tag::Entity> for Entity {
    fn to() -> RelationDef {
        super::article_tag::Relation::Tag.def()
    }
    fn via() -> Option<RelationDef> {
        Some(super::article_tag::Relation::Article.def().rev())
    }
}

显示带标签的文章列表

多对多关系定义好之后,可以在任意一方来查询。比如本例的通过文章列表来同时显示标签

如果要通过标签来查询关联的文章,只需要:

本章代码位于07/多对多分支