use crate::{ cast_text_as_enum, ActiveModelTrait, ColumnTrait, EntityTrait, Iterable, PrimaryKeyToColumn, QueryFilter, QueryTrait, }; use core::marker::PhantomData; use sea_query::{Expr, IntoIden, SimpleExpr, UpdateStatement}; /// Defines a structure to perform UPDATE query operations on a ActiveModel #[derive(Clone, Debug)] pub struct Update; /// Defines an UPDATE operation on one ActiveModel #[derive(Clone, Debug)] pub struct UpdateOne where A: ActiveModelTrait, { pub(crate) query: UpdateStatement, pub(crate) model: A, } /// Defines an UPDATE operation on multiple ActiveModels #[derive(Clone, Debug)] pub struct UpdateMany where E: EntityTrait, { pub(crate) query: UpdateStatement, pub(crate) entity: PhantomData, } impl Update { /// Update one ActiveModel /// /// ``` /// use sea_orm::{entity::*, query::*, tests_cfg::cake, DbBackend}; /// /// assert_eq!( /// Update::one(cake::ActiveModel { /// id: ActiveValue::set(1), /// name: ActiveValue::set("Apple Pie".to_owned()), /// }) /// .build(DbBackend::Postgres) /// .to_string(), /// r#"UPDATE "cake" SET "name" = 'Apple Pie' WHERE "cake"."id" = 1"#, /// ); /// ``` pub fn one(model: A) -> UpdateOne where E: EntityTrait, A: ActiveModelTrait, { UpdateOne { query: UpdateStatement::new() .table(A::Entity::default().table_ref()) .to_owned(), model, } .prepare_filters() .prepare_values() } /// Update many ActiveModel /// /// ``` /// use sea_orm::{entity::*, query::*, sea_query::Expr, tests_cfg::fruit, DbBackend}; /// /// assert_eq!( /// Update::many(fruit::Entity) /// .col_expr(fruit::Column::Name, Expr::value("Golden Apple")) /// .filter(fruit::Column::Name.contains("Apple")) /// .build(DbBackend::Postgres) /// .to_string(), /// r#"UPDATE "fruit" SET "name" = 'Golden Apple' WHERE "fruit"."name" LIKE '%Apple%'"#, /// ); /// ``` pub fn many(entity: E) -> UpdateMany where E: EntityTrait, { UpdateMany { query: UpdateStatement::new().table(entity.table_ref()).to_owned(), entity: PhantomData, } } } impl UpdateOne where A: ActiveModelTrait, { fn prepare_filters(mut self) -> Self { for key in ::PrimaryKey::iter() { let col = key.into_column(); let av = self.model.get(col); if av.is_set() || av.is_unchanged() { self = self.filter(col.eq(av.unwrap())); } else { panic!("PrimaryKey is not set"); } } self } fn prepare_values(mut self) -> Self { for col in ::Column::iter() { if ::PrimaryKey::from_column(col).is_some() { continue; } let av = self.model.get(col); if av.is_set() { let expr = cast_text_as_enum(Expr::val(av.into_value().unwrap()), &col); self.query.value(col, expr); } } self } } impl QueryFilter for UpdateOne where A: ActiveModelTrait, { type QueryStatement = UpdateStatement; fn query(&mut self) -> &mut UpdateStatement { &mut self.query } } impl QueryFilter for UpdateMany where E: EntityTrait, { type QueryStatement = UpdateStatement; fn query(&mut self) -> &mut UpdateStatement { &mut self.query } } impl QueryTrait for UpdateOne where A: ActiveModelTrait, { type QueryStatement = UpdateStatement; fn query(&mut self) -> &mut UpdateStatement { &mut self.query } fn as_query(&self) -> &UpdateStatement { &self.query } fn into_query(self) -> UpdateStatement { self.query } } impl QueryTrait for UpdateMany where E: EntityTrait, { type QueryStatement = UpdateStatement; fn query(&mut self) -> &mut UpdateStatement { &mut self.query } fn as_query(&self) -> &UpdateStatement { &self.query } fn into_query(self) -> UpdateStatement { self.query } } impl UpdateMany where E: EntityTrait, { /// Add the models to update to Self pub fn set(mut self, model: A) -> Self where A: ActiveModelTrait, { for col in E::Column::iter() { let av = model.get(col); if av.is_set() { self.query.value(col, av.unwrap()); } } self } /// Creates a [SimpleExpr] from a column pub fn col_expr(mut self, col: T, expr: SimpleExpr) -> Self where T: IntoIden, { self.query.value(col, expr); self } } #[cfg(test)] mod tests { use crate::tests_cfg::{cake, fruit}; use crate::{entity::*, query::*, DbBackend}; use sea_query::{Expr, Value}; #[test] fn update_1() { assert_eq!( Update::one(cake::ActiveModel { id: ActiveValue::set(1), name: ActiveValue::set("Apple Pie".to_owned()), }) .build(DbBackend::Postgres) .to_string(), r#"UPDATE "cake" SET "name" = 'Apple Pie' WHERE "cake"."id" = 1"#, ); } #[test] fn update_2() { assert_eq!( Update::one(fruit::ActiveModel { id: ActiveValue::set(1), name: ActiveValue::set("Orange".to_owned()), cake_id: ActiveValue::not_set(), }) .build(DbBackend::Postgres) .to_string(), r#"UPDATE "fruit" SET "name" = 'Orange' WHERE "fruit"."id" = 1"#, ); } #[test] fn update_3() { assert_eq!( Update::one(fruit::ActiveModel { id: ActiveValue::set(2), name: ActiveValue::unchanged("Apple".to_owned()), cake_id: ActiveValue::set(Some(3)), }) .build(DbBackend::Postgres) .to_string(), r#"UPDATE "fruit" SET "cake_id" = 3 WHERE "fruit"."id" = 2"#, ); } #[test] fn update_4() { assert_eq!( Update::many(fruit::Entity) .col_expr(fruit::Column::CakeId, Expr::value(Value::Int(None))) .filter(fruit::Column::Id.eq(2)) .build(DbBackend::Postgres) .to_string(), r#"UPDATE "fruit" SET "cake_id" = NULL WHERE "fruit"."id" = 2"#, ); } #[test] fn update_5() { assert_eq!( Update::many(fruit::Entity) .set(fruit::ActiveModel { name: ActiveValue::set("Apple".to_owned()), cake_id: ActiveValue::set(Some(3)), ..Default::default() }) .filter(fruit::Column::Id.eq(2)) .build(DbBackend::Postgres) .to_string(), r#"UPDATE "fruit" SET "name" = 'Apple', "cake_id" = 3 WHERE "fruit"."id" = 2"#, ); } #[test] fn update_6() { assert_eq!( Update::many(fruit::Entity) .set(fruit::ActiveModel { id: ActiveValue::set(3), ..Default::default() }) .filter(fruit::Column::Id.eq(2)) .build(DbBackend::Postgres) .to_string(), r#"UPDATE "fruit" SET "id" = 3 WHERE "fruit"."id" = 2"#, ); } }