实现所需的Trait以及创建并插入测试数据

在使用 SeaORM 之前,需要进行一些初始操作。本章我们将创建数据表、导入初始示例数据以及实现所需的 trait。最后,我们通过查询所有分类列表来验证这些操作是否正常运行。

准备数据

请创建一个 PostgreSQL 数据库,并将以下SQL导入其中:

CREATE TABLE categoies ( -- 分类
	id SERIAL PRIMARY KEY, -- 自增主键
	name VARCHAR(20) NOT NULL UNIQUE, -- 分类名称
	is_del BOOLEAN NOT NULL DEFAULT FALSE -- 是否删除
);

CREATE TABLE articles ( -- 文章
	id SERIAL PRIMARY KEY, -- 自增主键
	category_id INT NOT NULL REFERENCES categoies(id), -- 文章所属分类的ID,外键
	title VARCHAR(255) NOT NULL, -- 文章标题
	content TEXT NOT NULL, -- 文章内容
	dateline TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, -- 添加时间
	is_del BOOLEAN NOT NULL DEFAULT FALSE -- 是否删除
);

-- 插入示例数据
INSERT INTO categoies (id,name) VALUES
(1,'Rust'), (2,'Go'), (3,'Javascript');

编写实体并实现所需的 trait

下面,我们为分类(即categoies表)编写对应实体:

// src/entity/category.rs
use sea_orm::entity::prelude::*;
use serde::{Deserialize, Serialize};

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

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

impl RelationTrait for Relation {
    fn def(&self) -> sea_orm::RelationDef {
        panic!("没有定义关系")
    }

}

impl ActiveModelBehavior for ActiveModel {}

SeaORM 的实体和 Rust 的模块

SeaORM 将每一个数据表抽象成一个实体,而在 Rust 中,通常使用模块来表示实体。

通常,每个实体都会包含 EntityModelRelationActiveModel

Entity

官方文档

用于操作数据库。

Model

官方文档

用于定义数据表中的字段与Rust数据结构的映射关系。同时,在 SELECT 操作时,返回的也是 Model的实例。

Relation

官方文档

ActiveModel

官方文档

DeriveEntityModel

官方文档

这是一个【全能】的派生宏,它主要用于从指定的Model中,生成以下数据结构:

  • Entity
  • ActiveModel
  • Column
  • PrimaryKey

共享数据库连接

配置文件

WEB.ADDR=127.0.0.1:9527
DB.DSN="postgres://<用户名>:<密码>@pg.axum.rs:5432/axum_rs_seaorm"

共享状态结构体

// src/state.rs
use sea_orm::DatabaseConnection;

pub struct AppState {
    pub conn: DatabaseConnection,
}

给路由加上共享状态

// src/main.rs
dotenv().ok();
let cfg = config::Config::from_env().unwrap();
let conn = Database::connect(&cfg.db.dsn).await.unwrap();
let app = router::init().layer(Extension(Arc::new(state::AppState { conn })));

实现分类列表功能

从共享状态中获取数据库连接

// src/handler/mod.rs

fn get_conn<'a>(state: &'a AppState) -> &'a DatabaseConnection {
    &state.conn
}

从数据库中获取所有分类

// src/handler/category.rs
use std::sync::Arc;

use axum::Extension;
use sea_orm::{EntityTrait, QueryOrder};

use super::{get_conn, render, HtmlRespon};
use crate::{entity::category, state::AppState, view, AppError, Result};

pub async fn index(Extension(state): Extension<Arc<AppState>>) -> Result<HtmlRespon> {
    let handler_name = "category/index";
    let conn = get_conn(&state);
    let categies: Vec<category::Model> = category::Entity::find()
        .order_by_asc(category::Column::Id)
        .all(conn)
        .await
        .map_err(AppError::from)?;
    let tpl = view::CategoryTemplate { categies };
    render(tpl, handler_name)
}

我们重点来看这段代码:

let categies: Vec<category::Model> = category::Entity::find()
        .order_by_asc(category::Column::Id)
        .all(conn)
        .await
        .map_err(AppError::from)?;

category::Entity::find() -> Select<E>

构造一个用于查询的Select<E>对象。

Select<E>

文档

用于执行数据库SELECT操作的对象。

order_by_asc()

指定用于升序排序的字段。

category::Column::Id

指定字段名

all()

获取所有数据

将数据记录填充到视图中

// src/view.rs

#[derive(Template)]
#[template(path = "category.html")]
pub struct CategoryTemplate {
    pub categies: Vec<entity::category::Model>,
}

模板渲染

本章效果

category-list

本章代码位于01/实现trait及插入示例数据分支

要查看完整内容,请先登录