1use crate::{
2 error::*, ConnectionTrait, DeleteResult, EntityTrait, Iterable, PrimaryKeyArity,
3 PrimaryKeyToColumn, PrimaryKeyTrait, Value,
4};
5use async_trait::async_trait;
6use sea_query::{Nullable, ValueTuple};
7use std::fmt::Debug;
8
9pub use ActiveValue::NotSet;
10
11#[derive(Clone, Debug)]
40pub enum ActiveValue<V>
41where
42 V: Into<Value>,
43{
44 Set(V),
46 Unchanged(V),
48 NotSet,
50}
51
52#[allow(non_snake_case)]
54pub fn Set<V>(v: V) -> ActiveValue<V>
55where
56 V: Into<Value>,
57{
58 ActiveValue::set(v)
59}
60
61#[deprecated(
63 since = "0.5.0",
64 note = "Please use [`ActiveValue::NotSet`] or [`NotSet`]"
65)]
66#[allow(non_snake_case)]
67pub fn Unset<V>(_: Option<bool>) -> ActiveValue<V>
68where
69 V: Into<Value>,
70{
71 ActiveValue::not_set()
72}
73
74#[allow(non_snake_case)]
76pub fn Unchanged<V>(value: V) -> ActiveValue<V>
77where
78 V: Into<Value>,
79{
80 ActiveValue::unchanged(value)
81}
82
83#[async_trait]
87pub trait ActiveModelTrait: Clone + Debug {
88 type Entity: EntityTrait;
90
91 fn take(&mut self, c: <Self::Entity as EntityTrait>::Column) -> ActiveValue<Value>;
93
94 fn get(&self, c: <Self::Entity as EntityTrait>::Column) -> ActiveValue<Value>;
96
97 fn set(&mut self, c: <Self::Entity as EntityTrait>::Column, v: Value);
99
100 fn not_set(&mut self, c: <Self::Entity as EntityTrait>::Column);
102
103 fn is_not_set(&self, c: <Self::Entity as EntityTrait>::Column) -> bool;
105
106 fn default() -> Self;
108
109 fn reset(&mut self, c: <Self::Entity as EntityTrait>::Column);
112
113 fn reset_all(mut self) -> Self {
116 for col in <Self::Entity as EntityTrait>::Column::iter() {
117 self.reset(col);
118 }
119 self
120 }
121
122 #[allow(clippy::question_mark)]
128 fn get_primary_key_value(&self) -> Option<ValueTuple> {
129 let mut cols = <Self::Entity as EntityTrait>::PrimaryKey::iter();
130 macro_rules! next {
131 () => {
132 if let Some(col) = cols.next() {
133 if let Some(val) = self.get(col.into_column()).into_value() {
134 val
135 } else {
136 return None;
137 }
138 } else {
139 return None;
140 }
141 };
142 }
143 match <<<Self::Entity as EntityTrait>::PrimaryKey as PrimaryKeyTrait>::ValueType as PrimaryKeyArity>::ARITY {
144 1 => {
145 let s1 = next!();
146 Some(ValueTuple::One(s1))
147 }
148 2 => {
149 let s1 = next!();
150 let s2 = next!();
151 Some(ValueTuple::Two(s1, s2))
152 }
153 3 => {
154 let s1 = next!();
155 let s2 = next!();
156 let s3 = next!();
157 Some(ValueTuple::Three(s1, s2, s3))
158 }
159 len => {
160 let mut vec = Vec::with_capacity(len);
161 for _ in 0..len {
162 let s = next!();
163 vec.push(s);
164 }
165 Some(ValueTuple::Many(vec))
166 }
167 }
168 }
169
170 async fn insert<'a, C>(self, db: &'a C) -> Result<<Self::Entity as EntityTrait>::Model, DbErr>
277 where
278 <Self::Entity as EntityTrait>::Model: IntoActiveModel<Self>,
279 Self: ActiveModelBehavior + 'a,
280 C: ConnectionTrait,
281 {
282 let am = ActiveModelBehavior::before_save(self, db, true).await?;
283 let model = <Self::Entity as EntityTrait>::insert(am)
284 .exec_with_returning(db)
285 .await?;
286 Self::after_save(model, db, true).await
287 }
288
289 async fn update<'a, C>(self, db: &'a C) -> Result<<Self::Entity as EntityTrait>::Model, DbErr>
399 where
400 <Self::Entity as EntityTrait>::Model: IntoActiveModel<Self>,
401 Self: ActiveModelBehavior + 'a,
402 C: ConnectionTrait,
403 {
404 let am = ActiveModelBehavior::before_save(self, db, false).await?;
405 let model: <Self::Entity as EntityTrait>::Model = Self::Entity::update(am).exec(db).await?;
406 Self::after_save(model, db, false).await
407 }
408
409 async fn save<'a, C>(self, db: &'a C) -> Result<Self, DbErr>
412 where
413 <Self::Entity as EntityTrait>::Model: IntoActiveModel<Self>,
414 Self: ActiveModelBehavior + 'a,
415 C: ConnectionTrait,
416 {
417 let mut is_update = true;
418 for key in <Self::Entity as EntityTrait>::PrimaryKey::iter() {
419 let col = key.into_column();
420 if self.is_not_set(col) {
421 is_update = false;
422 break;
423 }
424 }
425 let res = if !is_update {
426 self.insert(db).await
427 } else {
428 self.update(db).await
429 }?;
430 Ok(res.into_active_model())
431 }
432
433 async fn delete<'a, C>(self, db: &'a C) -> Result<DeleteResult, DbErr>
477 where
478 Self: ActiveModelBehavior + 'a,
479 C: ConnectionTrait,
480 {
481 let am = ActiveModelBehavior::before_delete(self, db).await?;
482 let am_clone = am.clone();
483 let delete_res = Self::Entity::delete(am).exec(db).await?;
484 ActiveModelBehavior::after_delete(am_clone, db).await?;
485 Ok(delete_res)
486 }
487
488 #[cfg(feature = "with-json")]
492 fn set_from_json(&mut self, json: serde_json::Value) -> Result<(), DbErr>
493 where
494 <<Self as ActiveModelTrait>::Entity as EntityTrait>::Model: IntoActiveModel<Self>,
495 for<'de> <<Self as ActiveModelTrait>::Entity as EntityTrait>::Model:
496 serde::de::Deserialize<'de>,
497 {
498 use crate::Iterable;
499
500 let primary_key_values: Vec<(<Self::Entity as EntityTrait>::Column, ActiveValue<Value>)> =
502 <<Self::Entity as EntityTrait>::PrimaryKey>::iter()
503 .map(|pk| (pk.into_column(), self.take(pk.into_column())))
504 .collect();
505
506 *self = Self::from_json(json)?;
508
509 for (col, active_value) in primary_key_values {
511 match active_value {
512 ActiveValue::Unchanged(v) | ActiveValue::Set(v) => self.set(col, v),
513 NotSet => self.not_set(col),
514 }
515 }
516
517 Ok(())
518 }
519
520 #[cfg(feature = "with-json")]
522 fn from_json(json: serde_json::Value) -> Result<Self, DbErr>
523 where
524 <<Self as ActiveModelTrait>::Entity as EntityTrait>::Model: IntoActiveModel<Self>,
525 for<'de> <<Self as ActiveModelTrait>::Entity as EntityTrait>::Model:
526 serde::de::Deserialize<'de>,
527 {
528 use crate::{Iden, Iterable};
529
530 let json_keys: Vec<(<Self::Entity as EntityTrait>::Column, bool)> =
532 <<Self::Entity as EntityTrait>::Column>::iter()
533 .map(|col| (col, json.get(col.to_string()).is_some()))
534 .collect();
535
536 let model: <Self::Entity as EntityTrait>::Model =
538 serde_json::from_value(json).map_err(json_err)?;
539 let mut am = model.into_active_model();
540
541 for (col, json_key_exists) in json_keys {
543 match (json_key_exists, am.get(col)) {
544 (true, ActiveValue::Set(value) | ActiveValue::Unchanged(value)) => {
545 am.set(col, value);
546 }
547 _ => {
548 am.not_set(col);
549 }
550 }
551 }
552
553 Ok(am)
554 }
555
556 fn is_changed(&self) -> bool {
558 <Self::Entity as EntityTrait>::Column::iter()
559 .any(|col| self.get(col).is_set() && !self.get(col).is_unchanged())
560 }
561}
562
563#[allow(unused_variables)]
591#[async_trait]
592pub trait ActiveModelBehavior: ActiveModelTrait {
593 fn new() -> Self {
595 <Self as ActiveModelTrait>::default()
596 }
597
598 async fn before_save<C>(self, db: &C, insert: bool) -> Result<Self, DbErr>
600 where
601 C: ConnectionTrait,
602 {
603 Ok(self)
604 }
605
606 async fn after_save<C>(
608 model: <Self::Entity as EntityTrait>::Model,
609 db: &C,
610 insert: bool,
611 ) -> Result<<Self::Entity as EntityTrait>::Model, DbErr>
612 where
613 C: ConnectionTrait,
614 {
615 Ok(model)
616 }
617
618 async fn before_delete<C>(self, db: &C) -> Result<Self, DbErr>
620 where
621 C: ConnectionTrait,
622 {
623 Ok(self)
624 }
625
626 async fn after_delete<C>(self, db: &C) -> Result<Self, DbErr>
628 where
629 C: ConnectionTrait,
630 {
631 Ok(self)
632 }
633}
634
635pub trait IntoActiveModel<A>
637where
638 A: ActiveModelTrait,
639{
640 fn into_active_model(self) -> A;
642}
643
644impl<A> IntoActiveModel<A> for A
645where
646 A: ActiveModelTrait,
647{
648 fn into_active_model(self) -> A {
649 self
650 }
651}
652
653pub trait IntoActiveValue<V>
655where
656 V: Into<Value>,
657{
658 fn into_active_value(self) -> ActiveValue<V>;
660}
661
662impl<V> IntoActiveValue<Option<V>> for Option<V>
663where
664 V: IntoActiveValue<V> + Into<Value> + Nullable,
665{
666 fn into_active_value(self) -> ActiveValue<Option<V>> {
667 match self {
668 Some(value) => Set(Some(value)),
669 None => NotSet,
670 }
671 }
672}
673
674impl<V> IntoActiveValue<Option<V>> for Option<Option<V>>
675where
676 V: IntoActiveValue<V> + Into<Value> + Nullable,
677{
678 fn into_active_value(self) -> ActiveValue<Option<V>> {
679 match self {
680 Some(value) => Set(value),
681 None => NotSet,
682 }
683 }
684}
685
686macro_rules! impl_into_active_value {
687 ($ty: ty) => {
688 impl IntoActiveValue<$ty> for $ty {
689 fn into_active_value(self) -> ActiveValue<$ty> {
690 Set(self)
691 }
692 }
693 };
694}
695
696impl_into_active_value!(bool);
697impl_into_active_value!(i8);
698impl_into_active_value!(i16);
699impl_into_active_value!(i32);
700impl_into_active_value!(i64);
701impl_into_active_value!(u8);
702impl_into_active_value!(u16);
703impl_into_active_value!(u32);
704impl_into_active_value!(u64);
705impl_into_active_value!(f32);
706impl_into_active_value!(f64);
707impl_into_active_value!(&'static str);
708impl_into_active_value!(String);
709impl_into_active_value!(Vec<u8>);
710
711#[cfg(feature = "with-json")]
712#[cfg_attr(docsrs, doc(cfg(feature = "with-json")))]
713impl_into_active_value!(crate::prelude::Json);
714
715#[cfg(feature = "with-chrono")]
716#[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
717impl_into_active_value!(crate::prelude::Date);
718
719#[cfg(feature = "with-chrono")]
720#[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
721impl_into_active_value!(crate::prelude::Time);
722
723#[cfg(feature = "with-chrono")]
724#[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
725impl_into_active_value!(crate::prelude::DateTime);
726
727#[cfg(feature = "with-chrono")]
728#[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
729impl_into_active_value!(crate::prelude::DateTimeWithTimeZone);
730
731#[cfg(feature = "with-chrono")]
732#[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
733impl_into_active_value!(crate::prelude::DateTimeUtc);
734
735#[cfg(feature = "with-chrono")]
736#[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
737impl_into_active_value!(crate::prelude::DateTimeLocal);
738
739#[cfg(feature = "with-rust_decimal")]
740#[cfg_attr(docsrs, doc(cfg(feature = "with-rust_decimal")))]
741impl_into_active_value!(crate::prelude::Decimal);
742
743#[cfg(feature = "with-uuid")]
744#[cfg_attr(docsrs, doc(cfg(feature = "with-uuid")))]
745impl_into_active_value!(crate::prelude::Uuid);
746
747#[cfg(feature = "with-time")]
748#[cfg_attr(docsrs, doc(cfg(feature = "with-time")))]
749impl_into_active_value!(crate::prelude::TimeDate);
750
751#[cfg(feature = "with-time")]
752#[cfg_attr(docsrs, doc(cfg(feature = "with-time")))]
753impl_into_active_value!(crate::prelude::TimeTime);
754
755#[cfg(feature = "with-time")]
756#[cfg_attr(docsrs, doc(cfg(feature = "with-time")))]
757impl_into_active_value!(crate::prelude::TimeDateTime);
758
759#[cfg(feature = "with-time")]
760#[cfg_attr(docsrs, doc(cfg(feature = "with-time")))]
761impl_into_active_value!(crate::prelude::TimeDateTimeWithTimeZone);
762
763impl<V> Default for ActiveValue<V>
764where
765 V: Into<Value>,
766{
767 fn default() -> Self {
769 Self::NotSet
770 }
771}
772
773impl<V> ActiveValue<V>
774where
775 V: Into<Value>,
776{
777 pub fn set(value: V) -> Self {
779 Self::Set(value)
780 }
781
782 pub fn is_set(&self) -> bool {
784 matches!(self, Self::Set(_))
785 }
786
787 pub fn unchanged(value: V) -> Self {
789 Self::Unchanged(value)
790 }
791
792 pub fn is_unchanged(&self) -> bool {
794 matches!(self, Self::Unchanged(_))
795 }
796
797 pub fn not_set() -> Self {
799 Self::default()
800 }
801
802 pub fn is_not_set(&self) -> bool {
804 matches!(self, Self::NotSet)
805 }
806
807 pub fn take(&mut self) -> Option<V> {
810 match std::mem::take(self) {
811 ActiveValue::Set(value) | ActiveValue::Unchanged(value) => Some(value),
812 ActiveValue::NotSet => None,
813 }
814 }
815
816 pub fn unwrap(self) -> V {
822 match self {
823 ActiveValue::Set(value) | ActiveValue::Unchanged(value) => value,
824 ActiveValue::NotSet => panic!("Cannot unwrap ActiveValue::NotSet"),
825 }
826 }
827
828 pub fn into_value(self) -> Option<Value> {
830 match self {
831 ActiveValue::Set(value) | ActiveValue::Unchanged(value) => Some(value.into()),
832 ActiveValue::NotSet => None,
833 }
834 }
835
836 pub fn into_wrapped_value(self) -> ActiveValue<Value> {
838 match self {
839 Self::Set(value) => ActiveValue::set(value.into()),
840 Self::Unchanged(value) => ActiveValue::unchanged(value.into()),
841 Self::NotSet => ActiveValue::not_set(),
842 }
843 }
844
845 pub fn reset(&mut self) {
848 *self = match self.take() {
849 Some(value) => ActiveValue::Set(value),
850 None => ActiveValue::NotSet,
851 };
852 }
853
854 pub fn set_if_not_equals(&mut self, value: V)
882 where
883 V: PartialEq,
884 {
885 match self {
886 ActiveValue::Unchanged(current) if &value == current => {}
887 _ => *self = ActiveValue::Set(value),
888 }
889 }
890
891 pub fn try_as_ref(&self) -> Option<&V> {
905 match self {
906 ActiveValue::Set(value) | ActiveValue::Unchanged(value) => Some(value),
907 ActiveValue::NotSet => None,
908 }
909 }
910}
911
912impl<V> std::convert::AsRef<V> for ActiveValue<V>
913where
914 V: Into<Value>,
915{
916 fn as_ref(&self) -> &V {
922 match self {
923 ActiveValue::Set(value) | ActiveValue::Unchanged(value) => value,
924 ActiveValue::NotSet => panic!("Cannot borrow ActiveValue::NotSet"),
925 }
926 }
927}
928
929impl<V> PartialEq for ActiveValue<V>
930where
931 V: Into<Value> + std::cmp::PartialEq,
932{
933 fn eq(&self, other: &Self) -> bool {
934 match (self, other) {
935 (ActiveValue::Set(l), ActiveValue::Set(r)) => l == r,
936 (ActiveValue::Unchanged(l), ActiveValue::Unchanged(r)) => l == r,
937 (ActiveValue::NotSet, ActiveValue::NotSet) => true,
938 _ => false,
939 }
940 }
941}
942
943impl<V> From<ActiveValue<V>> for ActiveValue<Option<V>>
944where
945 V: Into<Value> + Nullable,
946{
947 fn from(value: ActiveValue<V>) -> Self {
948 match value {
949 ActiveValue::Set(value) => ActiveValue::set(Some(value)),
950 ActiveValue::Unchanged(value) => ActiveValue::unchanged(Some(value)),
951 ActiveValue::NotSet => ActiveValue::not_set(),
952 }
953 }
954}
955
956#[cfg(test)]
957mod tests {
958 use crate::{entity::*, tests_cfg::*, DbErr};
959 use pretty_assertions::assert_eq;
960
961 #[cfg(feature = "with-json")]
962 use serde_json::json;
963
964 #[test]
965 #[cfg(feature = "macros")]
966 fn test_derive_into_active_model_1() {
967 mod my_fruit {
968 pub use super::fruit::*;
969 use crate as sea_orm;
970 use crate::entity::prelude::*;
971
972 #[derive(DeriveIntoActiveModel)]
973 pub struct NewFruit {
974 pub name: String,
976 pub cake_id: i32,
978 }
979 }
980
981 assert_eq!(
982 my_fruit::NewFruit {
983 name: "Apple".to_owned(),
984 cake_id: 1,
985 }
986 .into_active_model(),
987 fruit::ActiveModel {
988 id: NotSet,
989 name: Set("Apple".to_owned()),
990 cake_id: Set(Some(1)),
991 }
992 );
993 }
994
995 #[test]
996 #[cfg(feature = "macros")]
997 fn test_derive_into_active_model_2() {
998 mod my_fruit {
999 pub use super::fruit::*;
1000 use crate as sea_orm;
1001 use crate::entity::prelude::*;
1002
1003 #[derive(DeriveIntoActiveModel)]
1004 pub struct UpdateFruit {
1005 pub cake_id: Option<Option<i32>>,
1006 }
1007 }
1008
1009 assert_eq!(
1010 my_fruit::UpdateFruit {
1011 cake_id: Some(Some(1)),
1012 }
1013 .into_active_model(),
1014 fruit::ActiveModel {
1015 id: NotSet,
1016 name: NotSet,
1017 cake_id: Set(Some(1)),
1018 }
1019 );
1020
1021 assert_eq!(
1022 my_fruit::UpdateFruit {
1023 cake_id: Some(None),
1024 }
1025 .into_active_model(),
1026 fruit::ActiveModel {
1027 id: NotSet,
1028 name: NotSet,
1029 cake_id: Set(None),
1030 }
1031 );
1032
1033 assert_eq!(
1034 my_fruit::UpdateFruit { cake_id: None }.into_active_model(),
1035 fruit::ActiveModel {
1036 id: NotSet,
1037 name: NotSet,
1038 cake_id: NotSet,
1039 }
1040 );
1041 }
1042
1043 #[test]
1044 #[cfg(feature = "macros")]
1045 fn test_derive_try_into_model_1() {
1046 mod my_fruit {
1047 use crate as sea_orm;
1048 use crate::entity::prelude::*;
1049
1050 #[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
1051 #[sea_orm(table_name = "fruit")]
1052 pub struct Model {
1053 #[sea_orm(primary_key)]
1054 pub id: i32,
1055 pub name: String,
1056 pub cake_id: Option<i32>,
1057 }
1058
1059 #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
1060 pub enum Relation {}
1061
1062 impl ActiveModelBehavior for ActiveModel {}
1063 }
1064 assert_eq!(
1065 my_fruit::ActiveModel {
1066 id: Set(1),
1067 name: Set("Pineapple".to_owned()),
1068 cake_id: Set(None),
1069 }
1070 .try_into_model()
1071 .unwrap(),
1072 my_fruit::Model {
1073 id: 1,
1074 name: "Pineapple".to_owned(),
1075 cake_id: None,
1076 }
1077 );
1078
1079 assert_eq!(
1080 my_fruit::ActiveModel {
1081 id: Set(2),
1082 name: Set("Apple".to_owned()),
1083 cake_id: Set(Some(1)),
1084 }
1085 .try_into_model()
1086 .unwrap(),
1087 my_fruit::Model {
1088 id: 2,
1089 name: "Apple".to_owned(),
1090 cake_id: Some(1),
1091 }
1092 );
1093
1094 assert_eq!(
1095 my_fruit::ActiveModel {
1096 id: Set(1),
1097 name: NotSet,
1098 cake_id: Set(None),
1099 }
1100 .try_into_model(),
1101 Err(DbErr::AttrNotSet(String::from("name")))
1102 );
1103
1104 assert_eq!(
1105 my_fruit::ActiveModel {
1106 id: Set(1),
1107 name: Set("Pineapple".to_owned()),
1108 cake_id: NotSet,
1109 }
1110 .try_into_model(),
1111 Err(DbErr::AttrNotSet(String::from("cake_id")))
1112 );
1113 }
1114
1115 #[test]
1116 #[cfg(feature = "macros")]
1117 fn test_derive_try_into_model_2() {
1118 mod my_fruit {
1119 use crate as sea_orm;
1120 use crate::entity::prelude::*;
1121
1122 #[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
1123 #[sea_orm(table_name = "fruit")]
1124 pub struct Model {
1125 #[sea_orm(primary_key)]
1126 pub id: i32,
1127 pub name: String,
1128 #[sea_orm(ignore)]
1129 pub cake_id: Option<i32>,
1130 }
1131
1132 #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
1133 pub enum Relation {}
1134
1135 impl ActiveModelBehavior for ActiveModel {}
1136 }
1137 assert_eq!(
1138 my_fruit::ActiveModel {
1139 id: Set(1),
1140 name: Set("Pineapple".to_owned()),
1141 }
1142 .try_into_model()
1143 .unwrap(),
1144 my_fruit::Model {
1145 id: 1,
1146 name: "Pineapple".to_owned(),
1147 cake_id: None,
1148 }
1149 );
1150 }
1151
1152 #[test]
1153 #[cfg(feature = "macros")]
1154 fn test_derive_try_into_model_3() {
1155 mod my_fruit {
1156 use crate as sea_orm;
1157 use crate::entity::prelude::*;
1158
1159 #[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
1160 #[sea_orm(table_name = "fruit")]
1161 pub struct Model {
1162 #[sea_orm(primary_key)]
1163 pub id: i32,
1164 #[sea_orm(ignore)]
1165 pub name: String,
1166 pub cake_id: Option<i32>,
1167 }
1168
1169 #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
1170 pub enum Relation {}
1171
1172 impl ActiveModelBehavior for ActiveModel {}
1173 }
1174 assert_eq!(
1175 my_fruit::ActiveModel {
1176 id: Set(1),
1177 cake_id: Set(Some(1)),
1178 }
1179 .try_into_model()
1180 .unwrap(),
1181 my_fruit::Model {
1182 id: 1,
1183 name: "".to_owned(),
1184 cake_id: Some(1),
1185 }
1186 );
1187 }
1188
1189 #[test]
1190 #[cfg(feature = "with-json")]
1191 #[should_panic(
1192 expected = r#"called `Result::unwrap()` on an `Err` value: Json("missing field `id`")"#
1193 )]
1194 fn test_active_model_set_from_json_1() {
1195 let mut cake: cake::ActiveModel = Default::default();
1196
1197 cake.set_from_json(json!({
1198 "name": "Apple Pie",
1199 }))
1200 .unwrap();
1201 }
1202
1203 #[test]
1204 #[cfg(feature = "with-json")]
1205 fn test_active_model_set_from_json_2() -> Result<(), DbErr> {
1206 let mut fruit: fruit::ActiveModel = Default::default();
1207
1208 fruit.set_from_json(json!({
1209 "name": "Apple",
1210 }))?;
1211 assert_eq!(
1212 fruit,
1213 fruit::ActiveModel {
1214 id: ActiveValue::NotSet,
1215 name: ActiveValue::Set("Apple".to_owned()),
1216 cake_id: ActiveValue::NotSet,
1217 }
1218 );
1219
1220 assert_eq!(
1221 fruit::ActiveModel::from_json(json!({
1222 "name": "Apple",
1223 }))?,
1224 fruit::ActiveModel {
1225 id: ActiveValue::NotSet,
1226 name: ActiveValue::Set("Apple".to_owned()),
1227 cake_id: ActiveValue::NotSet,
1228 }
1229 );
1230
1231 fruit.set_from_json(json!({
1232 "name": "Apple",
1233 "cake_id": null,
1234 }))?;
1235 assert_eq!(
1236 fruit,
1237 fruit::ActiveModel {
1238 id: ActiveValue::NotSet,
1239 name: ActiveValue::Set("Apple".to_owned()),
1240 cake_id: ActiveValue::Set(None),
1241 }
1242 );
1243
1244 fruit.set_from_json(json!({
1245 "id": null,
1246 "name": "Apple",
1247 "cake_id": 1,
1248 }))?;
1249 assert_eq!(
1250 fruit,
1251 fruit::ActiveModel {
1252 id: ActiveValue::NotSet,
1253 name: ActiveValue::Set("Apple".to_owned()),
1254 cake_id: ActiveValue::Set(Some(1)),
1255 }
1256 );
1257
1258 fruit.set_from_json(json!({
1259 "id": 2,
1260 "name": "Apple",
1261 "cake_id": 1,
1262 }))?;
1263 assert_eq!(
1264 fruit,
1265 fruit::ActiveModel {
1266 id: ActiveValue::NotSet,
1267 name: ActiveValue::Set("Apple".to_owned()),
1268 cake_id: ActiveValue::Set(Some(1)),
1269 }
1270 );
1271
1272 let mut fruit = fruit::ActiveModel {
1273 id: ActiveValue::Set(1),
1274 name: ActiveValue::NotSet,
1275 cake_id: ActiveValue::NotSet,
1276 };
1277 fruit.set_from_json(json!({
1278 "id": 8,
1279 "name": "Apple",
1280 "cake_id": 1,
1281 }))?;
1282 assert_eq!(
1283 fruit,
1284 fruit::ActiveModel {
1285 id: ActiveValue::Set(1),
1286 name: ActiveValue::Set("Apple".to_owned()),
1287 cake_id: ActiveValue::Set(Some(1)),
1288 }
1289 );
1290
1291 Ok(())
1292 }
1293
1294 #[smol_potat::test]
1295 #[cfg(feature = "with-json")]
1296 async fn test_active_model_set_from_json_3() -> Result<(), DbErr> {
1297 use crate::*;
1298
1299 let db = MockDatabase::new(DbBackend::Postgres)
1300 .append_exec_results([
1301 MockExecResult {
1302 last_insert_id: 1,
1303 rows_affected: 1,
1304 },
1305 MockExecResult {
1306 last_insert_id: 1,
1307 rows_affected: 1,
1308 },
1309 ])
1310 .append_query_results([
1311 [fruit::Model {
1312 id: 1,
1313 name: "Apple".to_owned(),
1314 cake_id: None,
1315 }],
1316 [fruit::Model {
1317 id: 2,
1318 name: "Orange".to_owned(),
1319 cake_id: Some(1),
1320 }],
1321 ])
1322 .into_connection();
1323
1324 let mut fruit: fruit::ActiveModel = Default::default();
1325 fruit.set_from_json(json!({
1326 "name": "Apple",
1327 }))?;
1328 fruit.save(&db).await?;
1329
1330 let mut fruit = fruit::ActiveModel {
1331 id: Set(2),
1332 ..Default::default()
1333 };
1334 fruit.set_from_json(json!({
1335 "id": 9,
1336 "name": "Orange",
1337 "cake_id": 1,
1338 }))?;
1339 fruit.save(&db).await?;
1340
1341 assert_eq!(
1342 db.into_transaction_log(),
1343 [
1344 Transaction::from_sql_and_values(
1345 DbBackend::Postgres,
1346 r#"INSERT INTO "fruit" ("name") VALUES ($1) RETURNING "id", "name", "cake_id""#,
1347 ["Apple".into()],
1348 ),
1349 Transaction::from_sql_and_values(
1350 DbBackend::Postgres,
1351 r#"UPDATE "fruit" SET "name" = $1, "cake_id" = $2 WHERE "fruit"."id" = $3 RETURNING "id", "name", "cake_id""#,
1352 ["Orange".into(), 1i32.into(), 2i32.into()],
1353 ),
1354 ]
1355 );
1356
1357 Ok(())
1358 }
1359
1360 #[test]
1361 fn test_active_model_is_changed() {
1362 let mut fruit: fruit::ActiveModel = Default::default();
1363 assert!(!fruit.is_changed());
1364
1365 fruit.set(fruit::Column::Name, "apple".into());
1366 assert!(fruit.is_changed());
1367 }
1368
1369 #[test]
1370 fn test_reset_1() {
1371 assert_eq!(
1372 fruit::Model {
1373 id: 1,
1374 name: "Apple".into(),
1375 cake_id: None,
1376 }
1377 .into_active_model(),
1378 fruit::ActiveModel {
1379 id: Unchanged(1),
1380 name: Unchanged("Apple".into()),
1381 cake_id: Unchanged(None)
1382 },
1383 );
1384
1385 assert_eq!(
1386 fruit::Model {
1387 id: 1,
1388 name: "Apple".into(),
1389 cake_id: None,
1390 }
1391 .into_active_model()
1392 .reset_all(),
1393 fruit::ActiveModel {
1394 id: Set(1),
1395 name: Set("Apple".into()),
1396 cake_id: Set(None)
1397 },
1398 );
1399
1400 assert_eq!(
1401 fruit::Model {
1402 id: 1,
1403 name: "Apple".into(),
1404 cake_id: Some(2),
1405 }
1406 .into_active_model(),
1407 fruit::ActiveModel {
1408 id: Unchanged(1),
1409 name: Unchanged("Apple".into()),
1410 cake_id: Unchanged(Some(2)),
1411 },
1412 );
1413
1414 assert_eq!(
1415 fruit::Model {
1416 id: 1,
1417 name: "Apple".into(),
1418 cake_id: Some(2),
1419 }
1420 .into_active_model()
1421 .reset_all(),
1422 fruit::ActiveModel {
1423 id: Set(1),
1424 name: Set("Apple".into()),
1425 cake_id: Set(Some(2)),
1426 },
1427 );
1428 }
1429
1430 #[smol_potat::test]
1431 async fn test_reset_2() -> Result<(), DbErr> {
1432 use crate::*;
1433
1434 let db = MockDatabase::new(DbBackend::Postgres)
1435 .append_exec_results(vec![
1436 MockExecResult {
1437 last_insert_id: 1,
1438 rows_affected: 1,
1439 },
1440 MockExecResult {
1441 last_insert_id: 1,
1442 rows_affected: 1,
1443 },
1444 ])
1445 .append_query_results(vec![
1446 vec![fruit::Model {
1447 id: 1,
1448 name: "Apple".to_owned(),
1449 cake_id: None,
1450 }],
1451 vec![fruit::Model {
1452 id: 1,
1453 name: "Apple".to_owned(),
1454 cake_id: None,
1455 }],
1456 ])
1457 .into_connection();
1458
1459 fruit::Model {
1460 id: 1,
1461 name: "Apple".into(),
1462 cake_id: None,
1463 }
1464 .into_active_model()
1465 .update(&db)
1466 .await?;
1467
1468 fruit::Model {
1469 id: 1,
1470 name: "Apple".into(),
1471 cake_id: None,
1472 }
1473 .into_active_model()
1474 .reset_all()
1475 .update(&db)
1476 .await?;
1477
1478 assert_eq!(
1479 db.into_transaction_log(),
1480 vec![
1481 Transaction::from_sql_and_values(
1482 DbBackend::Postgres,
1483 r#"SELECT "fruit"."id", "fruit"."name", "fruit"."cake_id" FROM "fruit" WHERE "fruit"."id" = $1 LIMIT $2"#,
1484 vec![1i32.into(), 1u64.into()],
1485 ),
1486 Transaction::from_sql_and_values(
1487 DbBackend::Postgres,
1488 r#"UPDATE "fruit" SET "name" = $1, "cake_id" = $2 WHERE "fruit"."id" = $3 RETURNING "id", "name", "cake_id""#,
1489 vec!["Apple".into(), Option::<i32>::None.into(), 1i32.into()],
1490 ),
1491 ]
1492 );
1493
1494 Ok(())
1495 }
1496}