跳到主要内容
版本:2.0.x

自定义 ActiveModel

创建包含模型部分字段的自定义结构体,实现 IntoActiveModel,可通过 into_active_model 方法转换为 ActiveModel。例如,可用作 REST API 中的表单提交。

IntoActiveValue 特征允许通过 into_active_value 方法将 Option<T> 转换为 ActiveValue<T>

// Define regular model as usual
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
#[sea_orm(table_name = "fruit")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
pub name: String,
pub cake_id: Option<i32>,
}

创建省略部分字段的新结构体。

use sea_orm::ActiveValue::NotSet;

#[derive(DeriveIntoActiveModel)]
pub struct NewFruit {
// id is omitted
pub name: String,
// it is required as opposed to optional in Model
pub cake_id: i32,
}

assert_eq!(
NewFruit {
name: "Apple".to_owned(),
cake_id: 1,
}
.into_active_model(),
fruit::ActiveModel {
id: NotSet,
name: Set("Apple".to_owned()),
cake_id: Set(Some(1)),
}
);

Option<Option<T>> 允许 Some(None) 将列更新为 NULL。

use sea_orm::ActiveValue::NotSet;

#[derive(DeriveIntoActiveModel)]
pub struct UpdateFruit {
pub cake_id: Option<Option<i32>>,
}

assert_eq!(
UpdateFruit { cake_id: Some(Some(1)) }.into_active_model(),
fruit::ActiveModel { id: NotSet, name: NotSet, cake_id: Set(Some(1)) }
);

assert_eq!(
UpdateFruit { cake_id: Some(None) }.into_active_model(),
fruit::ActiveModel { id: NotSet, name: NotSet, cake_id: Set(None) }
);

assert_eq!(
UpdateFruit { cake_id: None }.into_active_model(),
fruit::ActiveModel { id: NotSet, name: NotSet, cake_id: NotSet }
);

高级属性

Since 2.0.0

计算默认值与自动设置字段

在容器级别使用 set(field = "expr") 注入不属于结构体的字段,在 Option<T> 字段上使用 #[sea_orm(default = "expr")]None 时提供回退:

const SYSTEM_USER_ID: i32 = 0;

#[derive(DeriveIntoActiveModel)]
#[sea_orm(
active_model = "post::ActiveModel",
set(updated_at = "chrono::Utc::now()"),
set(version = "1")
)]
struct CreatePost {
title: String,
content: String,
#[sea_orm(default = "chrono::Utc::now()")]
published_at: Option<chrono::DateTime<chrono::Utc>>,
#[sea_orm(default = "SYSTEM_USER_ID")]
author_id: Option<i32>,
}
  • set(field = "expr") 接受任意 Rust 表达式:函数、常量、字面量。多个 set 属性会合并。
  • #[sea_orm(default = "expr")]Option<T> 字段提供回退:Some(v) 变为 Set(v)None 变为 Set(expr)
  • #[sea_orm(default)](裸)使用 Default::default() 作为回退。
  • active_model = "..." 显式指定目标 ActiveModel 类型。

忽略字段

使用 #[sea_orm(ignore)] 排除没有对应列的仅 DTO 字段:

#[derive(DeriveIntoActiveModel)]
#[sea_orm(active_model = "account::ActiveModel")]
struct UpdateAccount {
id: i32,
name: String,
#[sea_orm(ignore)]
audit_log: String,
}

穷举模式

添加 exhaustive 以移除 ..Default::default() 展开,强制每个 ActiveModel 字段都由结构体、setdefault 显式覆盖:

#[derive(DeriveIntoActiveModel)]
#[sea_orm(
active_model = "account::ActiveModel",
exhaustive,
set(updated_at = "chrono::Utc::now()")
)]
struct UpdateAccount {
id: i32,
#[sea_orm(default = "false")]
disabled: Option<bool>,
#[sea_orm(ignore)]
audit_log: String,
}

这可以在编译时保证没有字段意外保持为 NotSet

总结

场景属性ActiveModel 中的结果
直接提供字段正常声明字段Set(value)
字段不在结构体中但必须设置set(field = "expr")Set(expr)
可选字段并提供回退值Option<T> + #[sea_orm(default = "expr")]Some(v)Set(v), NoneSet(expr)
仅 DTO 字段,无对应列#[sea_orm(ignore)]排除
字段未声明且无默认值省略字段NotSet

PartialModel 转 ActiveModel

Since 1.1.0

DerivePartialModel 也可以通过 into_active_model 属性派生 IntoActiveModel。这可以复用你的 partial select 结构体进行写入:缺失的字段变为 NotSet

#[derive(DerivePartialModel)]
#[sea_orm(entity = "cake::Entity", into_active_model)]
struct PartialCake {
id: i32,
name: String,
}

let partial_cake = PartialCake {
id: 12,
name: "Lemon Drizzle".to_owned(),
};

assert_eq!(
cake::ActiveModel {
..partial_cake.into_active_model()
},
cake::ActiveModel {
id: Set(12),
name: Set("Lemon Drizzle".to_owned()),
..Default::default()
}
);

当 API 端点接收字段子集,并希望使用同一结构体进行查询和更新时,这很有用。