SeaORM简介
SeaORM 是一个关系型 ORM,用于帮助你像使用动态语言那样,在 Rust 中构建 Web 服务。实现所需的Trait以及创建并插入测试数据
在使用 SeaORM 之前,需要进行一些初始操作。本章我们将创建数据表、导入初始示例数据以及实现所需的 trait。最后,我们通过查询所有分类列表来验证这些操作是否正常运行。使用 SeaORM 查询数据
本章我们将讨论如何使用 SeaORM 进行查询:查询条件、分页、查询单条记录等。使用 SeaORM 插入数据
本章将讨论如何使用 SeaORM 添加记录。使用 SeaORM 修改数据
本章讨论使用 SeaORM 修改数据使用 SeaORM 删除数据
本章将讨论如何使用 SeaORM 实现删除。使用 SeaORM 操作一对多和多对一关系
正如其名,关系型数据库中的“关系”是很重要的部分。SeaORM 支持常见的数据关系,本章将讨论其中的一对多和多对一关系。SeaORM 的命令行工具和自动迁移
SeaORM 提供了一个命令行工具,可以快速生成实体。同时,还提供了自动迁移功能。SeaORM 操作多对多关系
本章将讨论多对多:一篇文章可以有多个标签,同样的,一个标签可以对应多篇文章。总结与作业
总结与作业
实现所需的Trait以及创建并插入测试数据
- 252917
- 2022-05-31 17:49:24
在使用 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
// 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 中,通常使用模块来表示实体。
通常,每个实体都会包含 Entity
、 Model
、Relation
和ActiveModel
。
Entity
用于操作数据库。
Model
Relation
用于定义数据表之间的关系。目前,我们不作任何定义,而是直接抛出一个panic
ActiveModel
也是数据表中字段与 Rust 数据结构的映射,不同于 Model
的是,ActiveModel
用于“写”操作,比如 UPDATE
、DELETE
等。
等等,这个 ActiveModel
是怎么来的?参考源码 可以发现,DeriveEntityModel
这个 derive 根据我们定义的Model
,生成了包括 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>,
}
模板渲染
<!-- templates/category.html -->
<table class="table">
<thead>
<tr>
<th>#</th>
<th>名称</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for category in categies %}
<tr>
<td>{{ category.id }}</td>
<td>{{ category.name }}</td>
<td>
<a href="/category/edit/{{ category.id }}" class="btn btn-primary btn-sm">修改</a>
<a href="/category/del/{{ category.id }}" class="btn btn-danger btn-sm" onclick="return confirm('确定删除“{{ category.name }}”?')">删除</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
本章效果
本章代码位于01/实现trait及插入示例数据分支