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