Skip to main content
Version: 0.12.x

Insert

Before diving into SeaORM insert we have to introduce ActiveValue and ActiveModel.

ActiveValue

A wrapper struct to capture the changes made to ActiveModel attributes.

use sea_orm::ActiveValue::{Set, NotSet, Unchanged};

// Set value
let _: ActiveValue<i32> = Set(10);

// NotSet value
let _: ActiveValue<i32> = NotSet;

// An `Unchanged` value
let v: ActiveValue<i32> = Unchanged(10);

// Convert `Unchanged` active value as `Set`
assert!(v.reset(), Set(10));

Model & ActiveModel

An ActiveModel has all the attributes of Model wrapped in ActiveValue.

You can use ActiveModel to insert a row with a subset of columns set.

let cheese: Option<cake::Model> = Cake::find_by_id(1).one(db).await?;

// Get Model
let model: cake::Model = cheese.unwrap();
assert_eq!(model.name, "Cheese Cake".to_owned());

// Into ActiveModel
let active_model: cake::ActiveModel = model.into();
assert_eq!(active_model.name, ActiveValue::unchanged("Cheese Cake".to_owned()));

Set ActiveModel from JSON Value

If you want to save user input into the database you can easily convert JSON value into ActiveModel. Note that you might want to skip deserializing JSON's primary key attribute, you can config that as shown below.

#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Serialize, Deserialize)]
#[sea_orm(table_name = "fruit")]
pub struct Model {
#[sea_orm(primary_key)]
#[serde(skip_deserializing)] // Skip deserializing
pub id: i32,
pub name: String,
pub cake_id: Option<i32>,
}

Set the attributes in ActiveModel with set_from_json method.

// A ActiveModel with primary key set
let mut fruit = fruit::ActiveModel {
id: ActiveValue::Set(1),
name: ActiveValue::NotSet,
cake_id: ActiveValue::NotSet,
};

// Note that this method will not alter the primary key values in ActiveModel
fruit.set_from_json(json!({
"id": 8,
"name": "Apple",
"cake_id": 1,
}))?;

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

Create a new ActiveModel from JSON value with the from_json method.

let fruit = fruit::ActiveModel::from_json(json!({
"name": "Apple",
}))?;

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

Checking if an ActiveModel is changed

You can check whether any field in an ActiveModel is Set with the is_changed method.

let mut fruit: fruit::ActiveModel = Default::default();
assert!(!fruit.is_changed());

fruit.set(fruit::Column::Name, "apple".into());
assert!(fruit.is_changed());

Convert ActiveModel back to Model

Using try_into_model method you can convert ActiveModel back to Model.

assert_eq!(
ActiveModel {
id: Set(2),
name: Set("Apple".to_owned()),
cake_id: Set(Some(1)),
}
.try_into_model()
.unwrap(),
Model {
id: 2,
name: "Apple".to_owned(),
cake_id: Some(1),
}
);

assert_eq!(
ActiveModel {
id: Set(1),
name: NotSet,
cake_id: Set(None),
}
.try_into_model(),
Err(DbErr::AttrNotSet(String::from("name")))
);

Insert One

Insert an active model and get back a fresh Model. Its value is retrieved from database, so any auto-generated fields will be populated.

let pear = fruit::ActiveModel {
name: Set("Pear".to_owned()),
..Default::default() // all other attributes are `NotSet`
};

let pear: fruit::Model = pear.insert(db).await?;

Insert an active model and get back the last insert id. Its type matches the model's primary key type, so it could be a tuple if the model has a composite primary key.

let pear = fruit::ActiveModel {
name: Set("Pear".to_owned()),
..Default::default() // all other attributes are `NotSet`
};

let res: InsertResult = fruit::Entity::insert(pear).exec(db).await?;
assert_eq!(res.last_insert_id, 28)

Insert Many

Insert many active models and get back the last insert id.

let apple = fruit::ActiveModel {
name: Set("Apple".to_owned()),
..Default::default()
};

let orange = fruit::ActiveModel {
name: Set("Orange".to_owned()),
..Default::default()
};

let res: InsertResult = Fruit::insert_many([apple, orange]).exec(db).await?;
assert_eq!(res.last_insert_id, 30)

Supplying an empty set to insert_many method will result in an error. However, you can change the behaviour with on_empty_do_nothing which wraps the InsertResult with a TryInsertResult.

let res = Bakery::insert_many(std::iter::empty())
.on_empty_do_nothing()
.exec(db)
.await;

assert!(matches!(res, Ok(TryInsertResult::Empty)));

On Conflict

Insert active model with on conflict behaviour.

let orange = cake::ActiveModel {
id: ActiveValue::set(2),
name: ActiveValue::set("Orange".to_owned()),
};

assert_eq!(
cake::Entity::insert(orange.clone())
.on_conflict(
// on conflict do nothing
sea_query::OnConflict::column(cake::Column::Name)
.do_nothing()
.to_owned()
)
.build(DbBackend::Postgres)
.to_string(),
r#"INSERT INTO "cake" ("id", "name") VALUES (2, 'Orange') ON CONFLICT ("name") DO NOTHING"#,
);

assert_eq!(
cake::Entity::insert(orange)
.on_conflict(
// on conflict do update
sea_query::OnConflict::column(cake::Column::Name)
.update_column(cake::Column::Name)
.to_owned()
)
.build(DbBackend::Postgres)
.to_string(),
r#"INSERT INTO "cake" ("id", "name") VALUES (2, 'Orange') ON CONFLICT ("name") DO UPDATE SET "name" = "excluded"."name""#,
);

Performing an upsert statement without inserting or updating any of the row will result in a DbErr::RecordNotInserted error.

// When `id` column have conflicting value, do nothing
let on_conflict = OnConflict::column(Column::Id).do_nothing().to_owned();

// Insert `1`, `2`, `3` into the table
let res = Entity::insert_many([
ActiveModel { id: Set(1) },
ActiveModel { id: Set(2) },
ActiveModel { id: Set(3) },
])
.on_conflict(on_conflict.clone())
.exec(db)
.await;

assert_eq!(res?.last_insert_id, 3);

// Insert `4` into the table together with the previous 3 rows
let res = Entity::insert_many([
ActiveModel { id: Set(1) },
ActiveModel { id: Set(2) },
ActiveModel { id: Set(3) },
ActiveModel { id: Set(4) },
])
.on_conflict(on_conflict.clone())
.exec(db)
.await;

assert_eq!(res?.last_insert_id, 4);

// Repeat last insert. Since all 4 rows already exist, this essentially did nothing.
// A `DbErr::RecordNotInserted` error will be thrown.
let res = Entity::insert_many([
ActiveModel { id: Set(1) },
ActiveModel { id: Set(2) },
ActiveModel { id: Set(3) },
ActiveModel { id: Set(4) },
])
.on_conflict(on_conflict)
.exec(db)
.await;

assert_eq!(res.err(), Some(DbErr::RecordNotInserted));

If you want RecordNotInserted to be an Ok instead of an error, call .do_nothing():

let res = Entity::insert_many([..])
.on_conflict(on_conflict)
.do_nothing()
.exec(db)
.await;

assert!(matches!(res, Ok(TryInsertResult::Conflicted)));