use super::{ActiveValue, ActiveValue::*};
use crate::{
ColumnTrait, Condition, ConnectionTrait, DbBackend, DeleteResult, EntityName, EntityTrait,
IdenStatic, Iterable, PrimaryKeyArity, PrimaryKeyToColumn, PrimaryKeyTrait, QueryFilter,
Related, RelatedSelfVia, RelationDef, RelationTrait, Value,
error::*,
query::{
clear_key_on_active_model, column_tuple_in_condition, get_key_from_active_model,
set_key_on_active_model,
},
};
use sea_query::ValueTuple;
use std::fmt::Debug;
pub trait ActiveModelTrait: Clone + Debug {
type Entity: EntityTrait;
fn take(&mut self, c: <Self::Entity as EntityTrait>::Column) -> ActiveValue<Value>;
fn get(&self, c: <Self::Entity as EntityTrait>::Column) -> ActiveValue<Value>;
fn set(&mut self, c: <Self::Entity as EntityTrait>::Column, v: Value) {
self.try_set(c, v)
.unwrap_or_else(|e| panic!("Failed to set value for {:?}: {e:?}", c.as_column_ref()))
}
fn set_if_not_equals(&mut self, c: <Self::Entity as EntityTrait>::Column, v: Value);
fn try_set(&mut self, c: <Self::Entity as EntityTrait>::Column, v: Value) -> Result<(), DbErr>;
fn not_set(&mut self, c: <Self::Entity as EntityTrait>::Column);
fn is_not_set(&self, c: <Self::Entity as EntityTrait>::Column) -> bool;
fn default() -> Self;
fn default_values() -> Self;
fn reset(&mut self, c: <Self::Entity as EntityTrait>::Column);
fn reset_all(mut self) -> Self {
for col in <Self::Entity as EntityTrait>::Column::iter() {
self.reset(col);
}
self
}
fn get_primary_key_value(&self) -> Option<ValueTuple> {
let mut cols = <Self::Entity as EntityTrait>::PrimaryKey::iter();
macro_rules! next {
() => {
self.get(cols.next()?.into_column()).into_value()?
};
}
match <<<Self::Entity as EntityTrait>::PrimaryKey as PrimaryKeyTrait>::ValueType as PrimaryKeyArity>::ARITY {
1 => {
let s1 = next!();
Some(ValueTuple::One(s1))
}
2 => {
let s1 = next!();
let s2 = next!();
Some(ValueTuple::Two(s1, s2))
}
3 => {
let s1 = next!();
let s2 = next!();
let s3 = next!();
Some(ValueTuple::Three(s1, s2, s3))
}
len => {
let mut vec = Vec::with_capacity(len);
for _ in 0..len {
let s = next!();
vec.push(s);
}
Some(ValueTuple::Many(vec))
}
}
}
fn insert<'a, C>(self, db: &'a C) -> Result<<Self::Entity as EntityTrait>::Model, DbErr>
where
<Self::Entity as EntityTrait>::Model: IntoActiveModel<Self>,
Self: ActiveModelBehavior,
C: ConnectionTrait,
{
let am = ActiveModelBehavior::before_save(self, db, true)?;
let model = <Self::Entity as EntityTrait>::insert(am).exec_with_returning(db)?;
Self::after_save(model, db, true)
}
fn update<'a, C>(self, db: &'a C) -> Result<<Self::Entity as EntityTrait>::Model, DbErr>
where
<Self::Entity as EntityTrait>::Model: IntoActiveModel<Self>,
Self: ActiveModelBehavior,
C: ConnectionTrait,
{
let am = ActiveModelBehavior::before_save(self, db, false)?;
let model: <Self::Entity as EntityTrait>::Model = Self::Entity::update(am).exec(db)?;
Self::after_save(model, db, false)
}
fn save<'a, C>(self, db: &'a C) -> Result<Self, DbErr>
where
<Self::Entity as EntityTrait>::Model: IntoActiveModel<Self>,
Self: ActiveModelBehavior,
C: ConnectionTrait,
{
let res = if !self.is_update() {
self.insert(db)
} else {
self.update(db)
}?;
Ok(res.into_active_model())
}
#[doc(hidden)]
fn is_update(&self) -> bool {
let mut is_update = true;
for key in <Self::Entity as EntityTrait>::PrimaryKey::iter() {
let col = key.into_column();
if self.is_not_set(col) {
is_update = false;
break;
}
}
is_update
}
fn delete<'a, C>(self, db: &'a C) -> Result<DeleteResult, DbErr>
where
Self: ActiveModelBehavior,
C: ConnectionTrait,
{
let am = ActiveModelBehavior::before_delete(self, db)?;
let am_clone = am.clone();
let delete_res = Self::Entity::delete(am).exec(db)?;
ActiveModelBehavior::after_delete(am_clone, db)?;
Ok(delete_res)
}
#[cfg(feature = "with-json")]
fn set_from_json(&mut self, json: serde_json::Value) -> Result<(), DbErr>
where
Self: crate::TryIntoModel<<Self::Entity as EntityTrait>::Model>,
<<Self as ActiveModelTrait>::Entity as EntityTrait>::Model: IntoActiveModel<Self>,
for<'de> <<Self as ActiveModelTrait>::Entity as EntityTrait>::Model:
serde::de::Deserialize<'de> + serde::Serialize,
{
use crate::Iterable;
let primary_key_values: Vec<(<Self::Entity as EntityTrait>::Column, ActiveValue<Value>)> =
<<Self::Entity as EntityTrait>::PrimaryKey>::iter()
.map(|pk| (pk.into_column(), self.take(pk.into_column())))
.collect();
*self = Self::from_json(json)?;
for (col, active_value) in primary_key_values {
match active_value {
ActiveValue::Unchanged(v) | ActiveValue::Set(v) => self.set(col, v),
NotSet => self.not_set(col),
}
}
Ok(())
}
#[cfg(feature = "with-json")]
fn from_json(mut json: serde_json::Value) -> Result<Self, DbErr>
where
Self: crate::TryIntoModel<<Self::Entity as EntityTrait>::Model>,
<<Self as ActiveModelTrait>::Entity as EntityTrait>::Model: IntoActiveModel<Self>,
for<'de> <<Self as ActiveModelTrait>::Entity as EntityTrait>::Model:
serde::de::Deserialize<'de> + serde::Serialize,
{
use crate::{IdenStatic, Iterable};
let serde_json::Value::Object(obj) = &json else {
return Err(DbErr::Json(format!(
"invalid type: expected JSON object for {}",
<<Self as ActiveModelTrait>::Entity as IdenStatic>::as_str(&Default::default())
)));
};
let mut json_keys: Vec<(<Self::Entity as EntityTrait>::Column, bool)> = Vec::new();
for col in <<Self::Entity as EntityTrait>::Column>::iter() {
let key = col.json_key();
let has_key = obj.contains_key(key);
json_keys.push((col, has_key));
}
let dummy_model = Self::default_values();
if let Ok(dummy_model) = dummy_model.try_into_model() {
if let Ok(mut dummy_json) = serde_json::to_value(&dummy_model) {
let serde_json::Value::Object(merged) = &mut dummy_json else {
unreachable!();
};
let serde_json::Value::Object(obj) = json else {
unreachable!();
};
for (key, value) in obj {
merged.insert(key, value);
}
json = dummy_json;
}
}
let model: <Self::Entity as EntityTrait>::Model =
serde_json::from_value(json).map_err(json_err)?;
let mut am = model.into_active_model();
for (col, json_key_exists) in json_keys {
match (json_key_exists, am.get(col)) {
(true, ActiveValue::Set(value) | ActiveValue::Unchanged(value)) => {
am.set(col, value);
}
_ => {
am.not_set(col);
}
}
}
Ok(am)
}
#[cfg(feature = "with-arrow")]
fn from_arrow(batch: &sea_orm_arrow::arrow::array::RecordBatch) -> Result<Vec<Self>, DbErr> {
use crate::{IdenStatic, Iterable, with_arrow::arrow_array_to_value};
let num_rows = batch.num_rows();
let mut results = Vec::with_capacity(num_rows);
for row in 0..num_rows {
let mut am = Self::default();
for col in <<Self::Entity as EntityTrait>::Column>::iter() {
let col_name = col.as_str();
if let Some(arrow_col) = batch.column_by_name(col_name) {
let col_def = col.def();
let col_type = col_def.get_column_type();
let value = arrow_array_to_value(arrow_col.as_ref(), col_type, row)?;
#[cfg(all(feature = "with-chrono", feature = "with-time"))]
{
use crate::with_arrow::{arrow_array_to_value_alt, is_datetime_column};
match am.try_set(col, value) {
Ok(()) => {}
Err(first_err) if is_datetime_column(col_type) => {
if let Some(alt_value) =
arrow_array_to_value_alt(arrow_col.as_ref(), col_type, row)?
{
am.try_set(col, alt_value)?;
} else {
return Err(first_err);
}
}
Err(e) => return Err(e),
}
}
#[cfg(not(all(feature = "with-chrono", feature = "with-time")))]
am.try_set(col, value)?;
} else {
am.not_set(col);
}
}
results.push(am);
}
Ok(results)
}
#[cfg(feature = "with-arrow")]
fn to_arrow(
models: &[Self],
schema: &sea_orm_arrow::arrow::datatypes::Schema,
) -> Result<sea_orm_arrow::arrow::array::RecordBatch, DbErr> {
use crate::{Iterable, with_arrow::option_values_to_arrow_array};
use std::sync::Arc;
let mut columns: Vec<Arc<dyn sea_orm_arrow::arrow::array::Array>> =
Vec::with_capacity(schema.fields().len());
for field in schema.fields() {
let field_name = field.name();
let entity_col =
<<Self::Entity as EntityTrait>::Column>::iter().find(|c| c.as_str() == field_name);
if let Some(col) = entity_col {
let values: Vec<Option<Value>> = models
.iter()
.map(|m| match m.get(col) {
ActiveValue::Set(v) | ActiveValue::Unchanged(v) => Some(v),
ActiveValue::NotSet => None,
})
.collect();
let array = option_values_to_arrow_array(&values, field.data_type())?;
columns.push(array);
} else {
let array =
sea_orm_arrow::arrow::array::new_null_array(field.data_type(), models.len());
columns.push(array);
}
}
sea_orm_arrow::arrow::array::RecordBatch::try_new(Arc::new(schema.clone()), columns)
.map_err(|e| DbErr::Type(format!("Failed to create RecordBatch: {e}")))
}
fn is_changed(&self) -> bool {
<Self::Entity as EntityTrait>::Column::iter()
.any(|col| matches!(self.get(col), ActiveValue::Set(_)))
}
#[doc(hidden)]
fn set_parent_key<R, AM>(&mut self, model: &AM) -> Result<(), DbErr>
where
R: EntityTrait,
AM: ActiveModelTrait<Entity = R>,
Self::Entity: Related<R>,
{
let rel_def = Self::Entity::to();
if rel_def.is_owner {
return Err(DbErr::Type(format!(
"Relation from {} to {} is not belongs_to",
<Self::Entity as Default>::default().as_str(),
<R as Default>::default().as_str()
)));
}
let values = get_key_from_active_model(&rel_def.to_col, model)?;
set_key_on_active_model(&rel_def.from_col, self, values)?;
Ok(())
}
#[doc(hidden)]
fn set_parent_key_for<R, AM>(
&mut self,
model: &AM,
rel: <Self::Entity as EntityTrait>::Relation,
) -> Result<(), DbErr>
where
R: EntityTrait,
AM: ActiveModelTrait<Entity = R>,
{
let rel_def = rel.def();
if rel_def.is_owner {
return Err(DbErr::Type(format!("Relation {rel:?} is not belongs_to")));
}
let values = get_key_from_active_model(&rel_def.to_col, model)?;
set_key_on_active_model(&rel_def.from_col, self, values)?;
Ok(())
}
#[doc(hidden)]
fn set_parent_key_for_def<R, AM>(
&mut self,
model: &AM,
rel_def: &RelationDef,
) -> Result<(), DbErr>
where
R: EntityTrait,
AM: ActiveModelTrait<Entity = R>,
{
if rel_def.is_owner {
return Err(DbErr::Type(format!(
"Relation {rel_def:?} is not belongs_to"
)));
}
let values = get_key_from_active_model(&rel_def.to_col, model)?;
set_key_on_active_model(&rel_def.from_col, self, values)?;
Ok(())
}
#[doc(hidden)]
fn set_parent_key_for_self_rev<AM>(
&mut self,
model: &AM,
rel: <Self::Entity as EntityTrait>::Relation,
) -> Result<(), DbErr>
where
AM: ActiveModelTrait<Entity = Self::Entity>,
{
let rel_def = rel.def();
if !rel_def.is_owner {
return Err(DbErr::Type(format!("Relation {rel:?} is not owner")));
}
let values = get_key_from_active_model(&rel_def.from_col, model)?;
set_key_on_active_model(&rel_def.to_col, self, values)?;
Ok(())
}
#[doc(hidden)]
fn clear_parent_key<R>(&mut self) -> Result<bool, DbErr>
where
R: EntityTrait,
Self::Entity: Related<R>,
{
let rel_def = Self::Entity::to();
if rel_def.is_owner {
return Err(DbErr::Type(format!(
"Relation from {} to {} is not belongs_to",
<Self::Entity as Default>::default().as_str(),
<R as Default>::default().as_str()
)));
}
clear_key_on_active_model(&rel_def.from_col, self)
}
#[doc(hidden)]
fn clear_parent_key_for_self_rev(
&mut self,
rel: <Self::Entity as EntityTrait>::Relation,
) -> Result<bool, DbErr> {
let rel_def = rel.def();
if !rel_def.is_owner {
return Err(DbErr::Type(format!("Relation {rel:?} is not owner")));
}
clear_key_on_active_model(&rel_def.to_col, self)
}
#[doc(hidden)]
fn get_parent_key<R>(&self) -> Result<ValueTuple, DbErr>
where
R: EntityTrait,
Self::Entity: Related<R>,
{
let rel_def = Self::Entity::to();
if rel_def.is_owner {
return Err(DbErr::Type(format!(
"Relation from {} to {} is not belongs_to",
<Self::Entity as Default>::default().as_str(),
<R as Default>::default().as_str()
)));
}
get_key_from_active_model(&rel_def.from_col, self)
}
#[doc(hidden)]
fn get_parent_key_for(
&self,
rel: <Self::Entity as EntityTrait>::Relation,
) -> Result<ValueTuple, DbErr> {
let rel_def = rel.def();
if rel_def.is_owner {
return Err(DbErr::Type(format!("Relation {rel:?} is not belongs_to")));
}
get_key_from_active_model(&rel_def.from_col, self)
}
#[doc(hidden)]
fn find_belongs_to_self(
&self,
rel: <Self::Entity as EntityTrait>::Relation,
db_backend: DbBackend,
) -> Result<crate::query::Select<Self::Entity>, DbErr> {
let rel_def = rel.def();
if !rel_def.is_owner {
return Err(DbErr::Type(format!(
"Relation {rel:?} is not has_one / has_many"
)));
}
let id = get_key_from_active_model(&rel_def.from_col, self)?;
Ok(Self::Entity::find().filter(
column_tuple_in_condition(
&<Self::Entity as Default>::default().table_ref(),
&rel_def.to_col,
&[id],
db_backend,
)
.expect(""),
))
}
#[doc(hidden)]
fn find_belongs_to_model<AM>(
rel_def: &RelationDef,
belongs_to: &AM,
db_backend: DbBackend,
) -> Result<crate::query::Select<Self::Entity>, DbErr>
where
AM: ActiveModelTrait,
{
if rel_def.is_owner {
return Err(DbErr::Type(format!(
"Relation {rel_def:?} is not belongs_to"
)));
}
let id = get_key_from_active_model(&rel_def.to_col, belongs_to)?;
Ok(<Self::Entity as EntityTrait>::find().filter(
column_tuple_in_condition(&rel_def.from_tbl, &rel_def.from_col, &[id], db_backend)
.expect(""),
))
}
fn find_related<R>(&self, _: R) -> crate::query::Select<R>
where
R: EntityTrait,
Self::Entity: Related<R>,
{
Self::Entity::find_related().belongs_to_active_model(self)
}
#[doc(hidden)]
fn find_related_of<AM>(&self, _: &[AM]) -> crate::query::Select<AM::Entity>
where
AM: ActiveModelTrait,
Self::Entity: Related<AM::Entity>,
{
self.find_related(AM::Entity::default())
}
#[doc(hidden)]
fn establish_links<J, R, RM, C>(
&self,
_: J,
related_models: &[RM],
delete_leftover: bool,
db: &C,
) -> Result<(), DbErr>
where
R: EntityTrait,
RM: ActiveModelTrait<Entity = R>,
J: EntityTrait + Related<R> + Related<Self::Entity>,
J::Model: IntoActiveModel<J::ActiveModel>,
J::ActiveModel: ActiveModelBehavior,
C: ConnectionTrait,
{
let left = <J as Related<Self::Entity>>::to();
let right = <J as Related<R>>::to();
establish_links::<_, J, _, C>(self, related_models, left, right, delete_leftover, db)
}
#[doc(hidden)]
fn establish_links_self<J, RM, C>(
&self,
_: J,
related_models: &[RM],
delete_leftover: bool,
db: &C,
) -> Result<(), DbErr>
where
RM: ActiveModelTrait<Entity = Self::Entity>,
J: EntityTrait,
J::Model: IntoActiveModel<J::ActiveModel>,
J::ActiveModel: ActiveModelBehavior,
C: ConnectionTrait,
Self::Entity: RelatedSelfVia<J>,
{
let left = <Self::Entity as RelatedSelfVia<J>>::via().rev();
let right = <Self::Entity as RelatedSelfVia<J>>::to();
establish_links::<_, J, _, C>(self, related_models, left, right, delete_leftover, db)
}
#[doc(hidden)]
fn establish_links_self_rev<J, RM, C>(
&self,
_: J,
related_models: &[RM],
delete_leftover: bool,
db: &C,
) -> Result<(), DbErr>
where
RM: ActiveModelTrait<Entity = Self::Entity>,
J: EntityTrait,
J::Model: IntoActiveModel<J::ActiveModel>,
J::ActiveModel: ActiveModelBehavior,
C: ConnectionTrait,
Self::Entity: RelatedSelfVia<J>,
{
let left = <Self::Entity as RelatedSelfVia<J>>::to();
let right = <Self::Entity as RelatedSelfVia<J>>::via().rev();
establish_links::<_, J, _, C>(self, related_models, left, right, delete_leftover, db)
}
#[doc(hidden)]
fn delete_links<J, C>(&self, _: J, db: &C) -> Result<DeleteResult, DbErr>
where
J: EntityTrait + Related<Self::Entity>,
C: ConnectionTrait,
{
let rel_def = <J as Related<Self::Entity>>::to();
let id = get_key_from_active_model(&rel_def.to_col, self)?;
J::delete_many()
.filter(
column_tuple_in_condition(
&rel_def.from_tbl,
&rel_def.from_col,
&[id],
db.get_database_backend(),
)
.expect(""),
)
.exec(db)
}
#[doc(hidden)]
fn delete_links_self<J, C>(&self, _: J, db: &C) -> Result<DeleteResult, DbErr>
where
J: EntityTrait,
C: ConnectionTrait,
Self::Entity: RelatedSelfVia<J>,
{
let left = <Self::Entity as RelatedSelfVia<J>>::via().rev();
let right = <Self::Entity as RelatedSelfVia<J>>::to();
let id = get_key_from_active_model(&left.to_col, self)?;
if left.to_col != right.to_col {
return Err(DbErr::Type("Expect Self Referencing Relation".into()));
}
J::delete_many()
.filter(
Condition::any()
.add(
column_tuple_in_condition(
&left.from_tbl,
&left.from_col,
std::slice::from_ref(&id),
db.get_database_backend(),
)
.expect(""),
)
.add(
column_tuple_in_condition(
&right.from_tbl,
&right.from_col,
std::slice::from_ref(&id),
db.get_database_backend(),
)
.expect(""),
),
)
.exec(db)
}
}
#[allow(unused_variables)]
pub trait ActiveModelBehavior: ActiveModelTrait {
fn new() -> Self {
<Self as ActiveModelTrait>::default()
}
fn before_save<C>(self, db: &C, insert: bool) -> Result<Self, DbErr>
where
C: ConnectionTrait,
{
Ok(self)
}
fn after_save<C>(
model: <Self::Entity as EntityTrait>::Model,
db: &C,
insert: bool,
) -> Result<<Self::Entity as EntityTrait>::Model, DbErr>
where
C: ConnectionTrait,
{
Ok(model)
}
fn before_delete<C>(self, db: &C) -> Result<Self, DbErr>
where
C: ConnectionTrait,
{
Ok(self)
}
fn after_delete<C>(self, db: &C) -> Result<Self, DbErr>
where
C: ConnectionTrait,
{
Ok(self)
}
}
pub trait IntoActiveModel<A>
where
A: ActiveModelTrait,
{
fn into_active_model(self) -> A;
}
impl<A> IntoActiveModel<A> for A
where
A: ActiveModelTrait,
{
fn into_active_model(self) -> A {
self
}
}
fn establish_links<EM, J, RM, C>(
model: &EM,
related_models: &[RM],
left: RelationDef,
right: RelationDef,
delete_leftover: bool,
db: &C,
) -> Result<(), DbErr>
where
EM: ActiveModelTrait,
RM: ActiveModelTrait,
J: EntityTrait,
J::Model: IntoActiveModel<J::ActiveModel>,
J::ActiveModel: ActiveModelBehavior,
C: ConnectionTrait,
{
let mut require_leftover = true;
if related_models.is_empty() {
require_leftover = false;
}
let primary_key = J::primary_key_identity();
if require_leftover
&& primary_key.fully_contains(&left.from_col)
&& primary_key.fully_contains(&right.from_col)
{
require_leftover = false;
}
let mut leftover = Vec::new();
if delete_leftover || require_leftover {
for item in <J::ActiveModel as ActiveModelTrait>::find_belongs_to_model(
&left,
model,
db.get_database_backend(),
)?
.all(db)?
{
let item = item.into_active_model();
let key = get_key_from_active_model(&right.from_col, &item)?;
leftover.push((item, key));
}
}
let leftover = leftover;
let mut via_models = Vec::new();
let mut all_keys = std::collections::HashSet::new();
for related_model in related_models {
let mut via: J::ActiveModel = ActiveModelBehavior::new();
via.set_parent_key_for_def(model, &left)?;
via.set_parent_key_for_def(related_model, &right)?;
let via_key = get_key_from_active_model(&right.from_col, &via)?;
if !leftover.iter().any(|t| t.1 == via_key) {
via_models.push(via);
}
if delete_leftover {
all_keys.insert(via_key);
}
}
if delete_leftover {
let mut to_delete = Vec::new();
for (leftover, key) in leftover {
if !all_keys.contains(&key) {
to_delete.push(
leftover
.get_primary_key_value()
.expect("item is a full model"),
);
}
}
if !to_delete.is_empty() {
J::delete_many()
.filter_by_value_tuples(&to_delete, db.get_database_backend())
.exec(db)?;
}
}
if !via_models.is_empty() {
J::insert_many(via_models)
.on_conflict_do_nothing()
.exec(db)?;
}
Ok(())
}
#[cfg(test)]
mod tests {
use crate::{DbErr, entity::*, tests_cfg::*};
use pretty_assertions::assert_eq;
#[cfg(feature = "with-json")]
use serde_json::json;
#[test]
#[cfg(feature = "macros")]
fn test_derive_into_active_model_1() {
mod my_fruit {
pub use super::fruit::*;
use crate as sea_orm;
use crate::entity::prelude::*;
#[derive(DeriveIntoActiveModel)]
pub struct NewFruit {
pub name: String,
pub cake_id: i32,
}
}
assert_eq!(
my_fruit::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)),
}
);
}
#[test]
#[cfg(feature = "macros")]
fn test_derive_into_active_model_2() {
use crate as sea_orm;
use crate::entity::prelude::*;
#[derive(DeriveIntoActiveModel)]
#[sea_orm(active_model = "fruit::ActiveModel")]
struct RequiredFruitName {
name: String,
}
assert_eq!(
RequiredFruitName {
name: "Apple Pie".to_owned(),
}
.into_active_model(),
fruit::ActiveModel {
id: NotSet,
name: Set("Apple Pie".to_owned()),
cake_id: NotSet,
}
);
#[derive(DeriveIntoActiveModel)]
#[sea_orm(active_model = "fruit::ActiveModel")]
struct OptionalFruitName {
name: Option<String>,
}
assert_eq!(
OptionalFruitName {
name: Some("Apple Pie".to_owned()),
}
.into_active_model(),
fruit::ActiveModel {
id: NotSet,
name: Set("Apple Pie".to_owned()),
cake_id: NotSet,
}
);
assert_eq!(
OptionalFruitName { name: None }.into_active_model(),
fruit::ActiveModel {
id: NotSet,
name: NotSet,
cake_id: NotSet,
}
);
#[derive(DeriveIntoActiveModel)]
#[sea_orm(active_model = "<fruit::Entity as EntityTrait>::ActiveModel")]
struct RequiredAndNotNullFruitCake {
cake_id: i32,
}
assert_eq!(
RequiredAndNotNullFruitCake { cake_id: 1 }.into_active_model(),
fruit::ActiveModel {
id: NotSet,
name: NotSet,
cake_id: Set(Some(1)),
}
);
#[derive(DeriveIntoActiveModel)]
#[sea_orm(active_model = "<fruit::Entity as EntityTrait>::ActiveModel")]
struct OptionalAndNotNullFruitCake {
cake_id: Option<i32>,
}
assert_eq!(
OptionalAndNotNullFruitCake { cake_id: Some(1) }.into_active_model(),
fruit::ActiveModel {
id: NotSet,
name: NotSet,
cake_id: Set(Some(1)),
}
);
assert_eq!(
OptionalAndNotNullFruitCake { cake_id: None }.into_active_model(),
fruit::ActiveModel {
id: NotSet,
name: NotSet,
cake_id: NotSet,
}
);
#[derive(DeriveIntoActiveModel)]
#[sea_orm(active_model = "<fruit::Entity as EntityTrait>::ActiveModel")]
struct OptionalAndNullableFruitCake {
cake_id: Option<Option<i32>>,
}
assert_eq!(
OptionalAndNullableFruitCake {
cake_id: Some(Some(1)),
}
.into_active_model(),
fruit::ActiveModel {
id: NotSet,
name: NotSet,
cake_id: Set(Some(1)),
}
);
assert_eq!(
OptionalAndNullableFruitCake {
cake_id: Some(None),
}
.into_active_model(),
fruit::ActiveModel {
id: NotSet,
name: NotSet,
cake_id: Set(None),
}
);
assert_eq!(
OptionalAndNullableFruitCake { cake_id: None }.into_active_model(),
fruit::ActiveModel {
id: NotSet,
name: NotSet,
cake_id: NotSet,
}
);
}
#[test]
#[cfg(feature = "macros")]
fn test_derive_into_active_model_set_single() {
use crate as sea_orm;
use crate::entity::prelude::*;
#[derive(DeriveIntoActiveModel)]
#[sea_orm(active_model = "fruit::ActiveModel", set(cake_id = "None"))]
struct NewFruit {
name: String,
}
assert_eq!(
NewFruit {
name: "Apple".to_owned(),
}
.into_active_model(),
fruit::ActiveModel {
id: NotSet,
name: Set("Apple".to_owned()),
cake_id: Set(None),
}
);
}
#[test]
#[cfg(feature = "macros")]
fn test_derive_into_active_model_set_multiple() {
use crate as sea_orm;
use crate::entity::prelude::*;
#[derive(DeriveIntoActiveModel)]
#[sea_orm(
active_model = "fruit::ActiveModel",
set(name = "String::from(\"cherry\")", cake_id = "None")
)]
struct IdOnlyFruit {
id: i32,
}
assert_eq!(
IdOnlyFruit { id: 1 }.into_active_model(),
fruit::ActiveModel {
id: Set(1),
name: Set("cherry".to_owned()),
cake_id: Set(None),
}
);
#[derive(DeriveIntoActiveModel)]
#[sea_orm(
active_model = "fruit::ActiveModel",
set(name = "String::from(\"cherry\")"),
set(cake_id = "None")
)]
struct IdOnlyFruit2 {
id: i32,
}
assert_eq!(
IdOnlyFruit2 { id: 1 }.into_active_model(),
fruit::ActiveModel {
id: Set(1),
name: Set("cherry".to_owned()),
cake_id: Set(None),
}
);
}
#[test]
#[cfg(feature = "macros")]
fn test_derive_into_active_model_set_separate_attrs() {
use crate as sea_orm;
use crate::entity::prelude::*;
#[derive(DeriveIntoActiveModel)]
#[sea_orm(
active_model = "fruit::ActiveModel",
set(name = "String::from(\"cherry\")")
)]
#[sea_orm(set(cake_id = "None"))]
struct IdOnlyFruit {
id: i32,
}
assert_eq!(
IdOnlyFruit { id: 1 }.into_active_model(),
fruit::ActiveModel {
id: Set(1),
name: Set("cherry".to_owned()),
cake_id: Set(None),
}
);
}
#[test]
#[cfg(feature = "macros")]
fn test_derive_into_active_model_ignore() {
use crate as sea_orm;
use crate::entity::prelude::*;
#[derive(DeriveIntoActiveModel)]
#[sea_orm(active_model = "fruit::ActiveModel")]
struct NewFruit {
name: String,
cake_id: i32,
#[sea_orm(ignore)]
_extra: String,
}
assert_eq!(
NewFruit {
name: "Apple".to_owned(),
cake_id: 1,
_extra: "ignored".to_owned(),
}
.into_active_model(),
fruit::ActiveModel {
id: NotSet,
name: Set("Apple".to_owned()),
cake_id: Set(Some(1)),
}
);
}
#[test]
#[cfg(feature = "macros")]
fn test_derive_into_active_model_skip() {
use crate as sea_orm;
use crate::entity::prelude::*;
#[derive(DeriveIntoActiveModel)]
#[sea_orm(active_model = "fruit::ActiveModel")]
struct NewFruit {
name: String,
#[sea_orm(skip)]
_extra: String,
}
assert_eq!(
NewFruit {
name: "Apple".to_owned(),
_extra: "skipped".to_owned(),
}
.into_active_model(),
fruit::ActiveModel {
id: NotSet,
name: Set("Apple".to_owned()),
cake_id: NotSet,
}
);
}
#[test]
#[cfg(feature = "macros")]
fn test_derive_into_active_model_set_and_ignore() {
use crate as sea_orm;
use crate::entity::prelude::*;
#[derive(DeriveIntoActiveModel)]
#[sea_orm(active_model = "fruit::ActiveModel", set(cake_id = "Some(42)"))]
struct NewFruit {
name: String,
#[sea_orm(ignore)]
_extra: String,
}
assert_eq!(
NewFruit {
name: "Apple".to_owned(),
_extra: "ignored".to_owned(),
}
.into_active_model(),
fruit::ActiveModel {
id: NotSet,
name: Set("Apple".to_owned()),
cake_id: Set(Some(42)),
}
);
}
#[test]
#[cfg(feature = "macros")]
fn test_derive_into_active_model_foreign_ignore() {
use serde::{Deserialize, Serialize};
use crate as sea_orm;
use crate::entity::prelude::*;
#[derive(DeriveIntoActiveModel, Serialize, Deserialize)]
#[sea_orm(active_model = "fruit::ActiveModel")]
struct NewFruit {
#[sea_orm(ignore)]
id: i32,
name: String,
#[serde(skip)]
cake_id: Option<i32>,
}
assert_eq!(
NewFruit {
id: 1.to_owned(),
name: "Apple".to_owned(),
cake_id: Some(42)
}
.into_active_model(),
fruit::ActiveModel {
id: NotSet,
name: Set("Apple".to_owned()),
cake_id: Set(Some(42)),
}
);
}
#[test]
#[cfg(feature = "macros")]
fn test_derive_into_active_model_exhaustive() {
use crate as sea_orm;
use crate::entity::prelude::*;
#[derive(DeriveIntoActiveModel)]
#[sea_orm(active_model = "fruit::ActiveModel", exhaustive, set(cake_id = "None"))]
struct FullFruit {
id: i32,
name: String,
}
assert_eq!(
FullFruit {
id: 1,
name: "Apple".to_owned(),
}
.into_active_model(),
fruit::ActiveModel {
id: Set(1),
name: Set("Apple".to_owned()),
cake_id: Set(None),
}
);
}
#[test]
#[cfg(feature = "macros")]
fn test_derive_into_active_model_multiple_sets() {
use crate as sea_orm;
use crate::entity::prelude::*;
const DEFULT_CAKE_ID: i32 = 1;
#[derive(DeriveIntoActiveModel)]
#[sea_orm(
active_model = "fruit::ActiveModel",
exhaustive,
set(cake_id = "Some(DEFULT_CAKE_ID)")
)]
struct FullFruit {
id: i32,
name: String,
}
assert_eq!(
FullFruit {
id: 1,
name: "Apple".to_owned(),
}
.into_active_model(),
fruit::ActiveModel {
id: Set(1),
name: Set("Apple".to_owned()),
cake_id: Set(Some(1)),
}
);
}
#[test]
#[cfg(feature = "macros")]
fn test_derive_into_active_model_field_empty_default() {
use crate as sea_orm;
use crate::entity::prelude::*;
#[derive(DeriveIntoActiveModel)]
#[sea_orm(active_model = "fruit::ActiveModel")]
struct NewFruit {
#[sea_orm(default)]
name: Option<String>,
}
assert_eq!(
NewFruit {
name: Some("Apple".to_owned()),
}
.into_active_model(),
fruit::ActiveModel {
id: NotSet,
name: Set("Apple".to_owned()),
cake_id: NotSet,
}
);
assert_eq!(
NewFruit { name: None }.into_active_model(),
fruit::ActiveModel {
id: NotSet,
name: Set("".to_owned()),
cake_id: NotSet,
}
);
}
#[test]
#[cfg(feature = "macros")]
fn test_derive_into_active_model_field_custom_option() {
use crate as sea_orm;
use crate::entity::prelude::*;
mod foreign_crate {
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum CustomOption<T> {
None,
Some(T),
}
impl From<CustomOption<String>> for Option<String> {
fn from(option: CustomOption<String>) -> Self {
match option {
CustomOption::None => Option::None,
CustomOption::Some(value) => value.into(),
}
}
}
}
use foreign_crate::CustomOption;
#[derive(DeriveIntoActiveModel)]
#[sea_orm(active_model = "fruit::ActiveModel")]
struct NewFruit {
#[sea_orm(default)]
name: CustomOption<String>,
}
assert_eq!(
NewFruit {
name: CustomOption::Some("Apple".to_owned()),
}
.into_active_model(),
fruit::ActiveModel {
id: NotSet,
name: Set("Apple".to_owned()),
cake_id: NotSet,
}
);
assert_eq!(
NewFruit {
name: CustomOption::None
}
.into_active_model(),
fruit::ActiveModel {
id: NotSet,
name: Set("".to_owned()),
cake_id: NotSet,
}
);
}
#[test]
#[cfg(feature = "macros")]
fn test_derive_into_active_model_field_default_some() {
use crate as sea_orm;
use crate::entity::prelude::*;
#[derive(DeriveIntoActiveModel)]
#[sea_orm(active_model = "fruit::ActiveModel")]
struct NewFruit {
#[sea_orm(default = "String::from(\"Unnamed\")")]
name: Option<String>,
}
assert_eq!(
NewFruit {
name: Some("Apple".to_owned()),
}
.into_active_model(),
fruit::ActiveModel {
id: NotSet,
name: Set("Apple".to_owned()),
cake_id: NotSet,
}
);
assert_eq!(
NewFruit { name: None }.into_active_model(),
fruit::ActiveModel {
id: NotSet,
name: Set("Unnamed".to_owned()),
cake_id: NotSet,
}
);
}
#[test]
#[cfg(feature = "macros")]
fn test_derive_into_active_model_field_default_with_set() {
use crate as sea_orm;
use crate::entity::prelude::*;
#[derive(DeriveIntoActiveModel)]
#[sea_orm(active_model = "fruit::ActiveModel", set(cake_id = "Some(99)"))]
struct NewFruit {
#[sea_orm(default = "String::from(\"Unnamed\")")]
name: Option<String>,
#[sea_orm(ignore)]
_extra: String,
}
assert_eq!(
NewFruit {
name: Some("Apple".to_owned()),
_extra: "ignored".to_owned(),
}
.into_active_model(),
fruit::ActiveModel {
id: NotSet,
name: Set("Apple".to_owned()),
cake_id: Set(Some(99)),
}
);
assert_eq!(
NewFruit {
name: None,
_extra: "ignored".to_owned(),
}
.into_active_model(),
fruit::ActiveModel {
id: NotSet,
name: Set("Unnamed".to_owned()),
cake_id: Set(Some(99)),
}
);
}
#[test]
#[cfg(feature = "macros")]
fn test_derive_into_active_model_field_default_exhaustive() {
use crate as sea_orm;
use crate::entity::prelude::*;
#[derive(DeriveIntoActiveModel)]
#[sea_orm(active_model = "fruit::ActiveModel", exhaustive, set(cake_id = "None"))]
struct NewFruit {
id: i32,
#[sea_orm(default = "String::from(\"Unnamed\")")]
name: Option<String>,
}
assert_eq!(
NewFruit {
id: 1,
name: Some("Apple".to_owned()),
}
.into_active_model(),
fruit::ActiveModel {
id: Set(1),
name: Set("Apple".to_owned()),
cake_id: Set(None),
}
);
assert_eq!(
NewFruit { id: 2, name: None }.into_active_model(),
fruit::ActiveModel {
id: Set(2),
name: Set("Unnamed".to_owned()),
cake_id: Set(None),
}
);
}
#[test]
#[cfg(feature = "macros")]
fn test_derive_try_into_model_1() {
mod my_fruit {
use crate as sea_orm;
use crate::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
#[sea_orm(table_name = "fruit")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
pub name: String,
pub cake_id: Option<i32>,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {}
impl ActiveModelBehavior for ActiveModel {}
}
assert_eq!(
my_fruit::ActiveModel {
id: Set(1),
name: Set("Pineapple".to_owned()),
cake_id: Set(None),
}
.try_into_model()
.unwrap(),
my_fruit::Model {
id: 1,
name: "Pineapple".to_owned(),
cake_id: None,
}
);
assert_eq!(
my_fruit::ActiveModel {
id: Set(2),
name: Set("Apple".to_owned()),
cake_id: Set(Some(1)),
}
.try_into_model()
.unwrap(),
my_fruit::Model {
id: 2,
name: "Apple".to_owned(),
cake_id: Some(1),
}
);
assert_eq!(
my_fruit::ActiveModel {
id: Set(1),
name: NotSet,
cake_id: Set(None),
}
.try_into_model(),
Err(DbErr::AttrNotSet(String::from("name")))
);
assert_eq!(
my_fruit::ActiveModel {
id: Set(1),
name: Set("Pineapple".to_owned()),
cake_id: NotSet,
}
.try_into_model(),
Err(DbErr::AttrNotSet(String::from("cake_id")))
);
}
#[test]
#[cfg(feature = "macros")]
fn test_derive_try_into_model_2() {
mod my_fruit {
use crate as sea_orm;
use crate::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
#[sea_orm(table_name = "fruit")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
pub name: String,
#[sea_orm(ignore)]
pub cake_id: Option<i32>,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {}
impl ActiveModelBehavior for ActiveModel {}
}
assert_eq!(
my_fruit::ActiveModel {
id: Set(1),
name: Set("Pineapple".to_owned()),
}
.try_into_model()
.unwrap(),
my_fruit::Model {
id: 1,
name: "Pineapple".to_owned(),
cake_id: None,
}
);
}
#[test]
#[cfg(feature = "macros")]
fn test_derive_try_into_model_3() {
mod my_fruit {
use crate as sea_orm;
use crate::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
#[sea_orm(table_name = "fruit")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
#[sea_orm(ignore)]
pub name: String,
pub cake_id: Option<i32>,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {}
impl ActiveModelBehavior for ActiveModel {}
}
assert_eq!(
my_fruit::ActiveModel {
id: Set(1),
cake_id: Set(Some(1)),
}
.try_into_model()
.unwrap(),
my_fruit::Model {
id: 1,
name: "".to_owned(),
cake_id: Some(1),
}
);
}
#[test]
#[cfg(feature = "with-json")]
fn test_active_model_set_from_json_1() {
assert_eq!(
cake::ActiveModel::from_json(json!({
"id": 1,
"name": "Apple Pie",
}))
.unwrap(),
cake::ActiveModel {
id: Set(1),
name: Set("Apple Pie".to_owned()),
}
);
assert_eq!(
cake::ActiveModel::from_json(json!({
"id": 1,
}))
.unwrap(),
cake::ActiveModel {
id: Set(1),
name: NotSet,
}
);
assert_eq!(
cake::ActiveModel::from_json(json!({
"name": "Apple Pie",
}))
.unwrap(),
cake::ActiveModel {
id: NotSet,
name: Set("Apple Pie".to_owned()),
}
);
let mut cake: cake::ActiveModel = Default::default();
cake.set_from_json(json!({
"name": "Apple Pie",
}))
.unwrap();
assert_eq!(
cake,
cake::ActiveModel {
id: NotSet,
name: Set("Apple Pie".to_owned()),
}
);
}
#[test]
#[cfg(feature = "with-json")]
fn test_active_model_set_from_json_2() -> Result<(), DbErr> {
let mut fruit: fruit::ActiveModel = Default::default();
fruit.set_from_json(json!({
"name": "Apple",
}))?;
assert_eq!(
fruit,
fruit::ActiveModel {
id: ActiveValue::NotSet,
name: ActiveValue::Set("Apple".to_owned()),
cake_id: ActiveValue::NotSet,
}
);
assert_eq!(
fruit::ActiveModel::from_json(json!({
"name": "Apple",
}))?,
fruit::ActiveModel {
id: ActiveValue::NotSet,
name: ActiveValue::Set("Apple".to_owned()),
cake_id: ActiveValue::NotSet,
}
);
fruit.set_from_json(json!({
"name": "Apple",
"cake_id": null,
}))?;
assert_eq!(
fruit,
fruit::ActiveModel {
id: ActiveValue::NotSet,
name: ActiveValue::Set("Apple".to_owned()),
cake_id: ActiveValue::Set(None),
}
);
fruit.set_from_json(json!({
"id": null,
"name": "Apple",
"cake_id": 1,
}))?;
assert_eq!(
fruit,
fruit::ActiveModel {
id: ActiveValue::NotSet,
name: ActiveValue::Set("Apple".to_owned()),
cake_id: ActiveValue::Set(Some(1)),
}
);
fruit.set_from_json(json!({
"id": 2,
"name": "Apple",
"cake_id": 1,
}))?;
assert_eq!(
fruit,
fruit::ActiveModel {
id: ActiveValue::NotSet,
name: ActiveValue::Set("Apple".to_owned()),
cake_id: ActiveValue::Set(Some(1)),
}
);
let mut fruit = fruit::ActiveModel {
id: ActiveValue::Set(1),
name: ActiveValue::NotSet,
cake_id: ActiveValue::NotSet,
};
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)),
}
);
Ok(())
}
#[test]
#[cfg(feature = "with-json")]
fn test_active_model_set_from_json_3() -> Result<(), DbErr> {
use crate::*;
let db = MockDatabase::new(DbBackend::Postgres)
.append_exec_results([
MockExecResult {
last_insert_id: 1,
rows_affected: 1,
},
MockExecResult {
last_insert_id: 1,
rows_affected: 1,
},
])
.append_query_results([
[fruit::Model {
id: 1,
name: "Apple".to_owned(),
cake_id: None,
}],
[fruit::Model {
id: 2,
name: "Orange".to_owned(),
cake_id: Some(1),
}],
])
.into_connection();
let mut fruit: fruit::ActiveModel = Default::default();
fruit.set_from_json(json!({
"name": "Apple",
}))?;
fruit.save(&db)?;
let mut fruit = fruit::ActiveModel {
id: Set(2),
..Default::default()
};
fruit.set_from_json(json!({
"id": 9,
"name": "Orange",
"cake_id": 1,
}))?;
fruit.save(&db)?;
assert_eq!(
db.into_transaction_log(),
[
Transaction::from_sql_and_values(
DbBackend::Postgres,
r#"INSERT INTO "fruit" ("name") VALUES ($1) RETURNING "id", "name", "cake_id""#,
["Apple".into()],
),
Transaction::from_sql_and_values(
DbBackend::Postgres,
r#"UPDATE "fruit" SET "name" = $1, "cake_id" = $2 WHERE "fruit"."id" = $3 RETURNING "id", "name", "cake_id""#,
["Orange".into(), 1i32.into(), 2i32.into()],
),
]
);
Ok(())
}
#[test]
fn test_active_model_is_changed() {
let mut fruit: fruit::ActiveModel = Default::default();
assert!(!fruit.is_changed());
fruit.set(fruit::Column::Name, "apple".into());
assert!(fruit.is_changed());
let mut fruit = fruit::Model {
id: 1,
name: "".into(),
cake_id: None,
};
fruit.set("name".parse().unwrap(), "orange".into());
assert_eq!(fruit.name, "orange");
}
#[test]
fn test_reset_1() {
assert_eq!(
fruit::Model {
id: 1,
name: "Apple".into(),
cake_id: None,
}
.into_active_model(),
fruit::ActiveModel {
id: Unchanged(1),
name: Unchanged("Apple".into()),
cake_id: Unchanged(None)
},
);
assert_eq!(
fruit::Model {
id: 1,
name: "Apple".into(),
cake_id: None,
}
.into_active_model()
.reset_all(),
fruit::ActiveModel {
id: Set(1),
name: Set("Apple".into()),
cake_id: Set(None)
},
);
assert_eq!(
fruit::Model {
id: 1,
name: "Apple".into(),
cake_id: Some(2),
}
.into_active_model(),
fruit::ActiveModel {
id: Unchanged(1),
name: Unchanged("Apple".into()),
cake_id: Unchanged(Some(2)),
},
);
assert_eq!(
fruit::Model {
id: 1,
name: "Apple".into(),
cake_id: Some(2),
}
.into_active_model()
.reset_all(),
fruit::ActiveModel {
id: Set(1),
name: Set("Apple".into()),
cake_id: Set(Some(2)),
},
);
}
#[test]
fn test_reset_2() -> Result<(), DbErr> {
use crate::*;
let db = MockDatabase::new(DbBackend::Postgres)
.append_exec_results(vec![
MockExecResult {
last_insert_id: 1,
rows_affected: 1,
},
MockExecResult {
last_insert_id: 1,
rows_affected: 1,
},
])
.append_query_results(vec![
vec![fruit::Model {
id: 1,
name: "Apple".to_owned(),
cake_id: None,
}],
vec![fruit::Model {
id: 1,
name: "Apple".to_owned(),
cake_id: None,
}],
])
.into_connection();
fruit::Model {
id: 1,
name: "Apple".into(),
cake_id: None,
}
.into_active_model()
.update(&db)?;
fruit::Model {
id: 1,
name: "Apple".into(),
cake_id: None,
}
.into_active_model()
.reset_all()
.update(&db)?;
assert_eq!(
db.into_transaction_log(),
vec![
Transaction::from_sql_and_values(
DbBackend::Postgres,
r#"SELECT "fruit"."id", "fruit"."name", "fruit"."cake_id" FROM "fruit" WHERE "fruit"."id" = $1 LIMIT $2"#,
vec![1i32.into(), 1u64.into()],
),
Transaction::from_sql_and_values(
DbBackend::Postgres,
r#"UPDATE "fruit" SET "name" = $1, "cake_id" = $2 WHERE "fruit"."id" = $3 RETURNING "id", "name", "cake_id""#,
vec!["Apple".into(), Option::<i32>::None.into(), 1i32.into()],
),
]
);
Ok(())
}
#[test]
fn test_active_model_default_values() {
assert_eq!(
fruit::ActiveModel::default_values(),
fruit::ActiveModel {
id: Set(0),
name: Set("".into()),
cake_id: Set(None),
},
);
assert_eq!(
lunch_set::ActiveModel::default_values(),
lunch_set::ActiveModel {
id: Set(0),
name: Set("".into()),
tea: NotSet,
},
);
}
#[test]
fn test_active_model_set_parent_key() {
let mut fruit = fruit::Model {
id: 2,
name: "F".into(),
cake_id: None,
}
.into_active_model();
let cake = cake::Model {
id: 4,
name: "C".into(),
}
.into_active_model();
fruit.set_parent_key(&cake).unwrap();
assert_eq!(
fruit,
fruit::ActiveModel {
id: Unchanged(2),
name: Unchanged("F".into()),
cake_id: Set(Some(4)),
}
);
assert!(fruit.clear_parent_key::<cake::Entity>().unwrap());
assert_eq!(
fruit,
fruit::ActiveModel {
id: Unchanged(2),
name: Unchanged("F".into()),
cake_id: Set(None),
}
);
let mut cake_filling = cake_filling::ActiveModel::new();
cake_filling.set_parent_key(&cake).unwrap();
assert_eq!(
cake_filling,
cake_filling::ActiveModel {
cake_id: Set(4),
filling_id: NotSet,
}
);
}
}