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::{IdenStatic, Iterable};
511
512 let serde_json::Value::Object(obj) = &json else {
513 return Err(DbErr::Json(format!(
514 "invalid type: expected JSON object for {}",
515 <<Self as ActiveModelTrait>::Entity as IdenStatic>::as_str(&Default::default())
516 )));
517 };
518
519 let mut json_keys: Vec<(<Self::Entity as EntityTrait>::Column, bool)> = Vec::new();
521
522 for col in <<Self::Entity as EntityTrait>::Column>::iter() {
523 let key = col.as_str();
524 let has_key = obj.contains_key(key);
525 json_keys.push((col, has_key));
526 }
527
528 let model: <Self::Entity as EntityTrait>::Model =
530 serde_json::from_value(json).map_err(json_err)?;
531 let mut am = model.into_active_model();
532
533 for (col, json_key_exists) in json_keys {
535 match (json_key_exists, am.get(col)) {
536 (true, ActiveValue::Set(value) | ActiveValue::Unchanged(value)) => {
537 am.set(col, value);
538 }
539 _ => {
540 am.not_set(col);
541 }
542 }
543 }
544
545 Ok(am)
546 }
547
548 fn is_changed(&self) -> bool {
550 <Self::Entity as EntityTrait>::Column::iter()
551 .any(|col| self.get(col).is_set() && !self.get(col).is_unchanged())
552 }
553}
554
555#[allow(unused_variables)]
583#[async_trait]
584pub trait ActiveModelBehavior: ActiveModelTrait {
585 fn new() -> Self {
587 <Self as ActiveModelTrait>::default()
588 }
589
590 async fn before_save<C>(self, db: &C, insert: bool) -> Result<Self, DbErr>
592 where
593 C: ConnectionTrait,
594 {
595 Ok(self)
596 }
597
598 async fn after_save<C>(
600 model: <Self::Entity as EntityTrait>::Model,
601 db: &C,
602 insert: bool,
603 ) -> Result<<Self::Entity as EntityTrait>::Model, DbErr>
604 where
605 C: ConnectionTrait,
606 {
607 Ok(model)
608 }
609
610 async fn before_delete<C>(self, db: &C) -> Result<Self, DbErr>
612 where
613 C: ConnectionTrait,
614 {
615 Ok(self)
616 }
617
618 async fn after_delete<C>(self, db: &C) -> Result<Self, DbErr>
620 where
621 C: ConnectionTrait,
622 {
623 Ok(self)
624 }
625}
626
627pub trait IntoActiveModel<A>
629where
630 A: ActiveModelTrait,
631{
632 fn into_active_model(self) -> A;
634}
635
636impl<A> IntoActiveModel<A> for A
637where
638 A: ActiveModelTrait,
639{
640 fn into_active_model(self) -> A {
641 self
642 }
643}
644
645pub trait IntoActiveValue<V>
647where
648 V: Into<Value>,
649{
650 fn into_active_value(self) -> ActiveValue<V>;
652}
653
654impl<V> IntoActiveValue<Option<V>> for Option<V>
655where
656 V: IntoActiveValue<V> + Into<Value> + Nullable,
657{
658 fn into_active_value(self) -> ActiveValue<Option<V>> {
659 match self {
660 Some(value) => Set(Some(value)),
661 None => NotSet,
662 }
663 }
664}
665
666impl<V> IntoActiveValue<Option<V>> for Option<Option<V>>
667where
668 V: IntoActiveValue<V> + Into<Value> + Nullable,
669{
670 fn into_active_value(self) -> ActiveValue<Option<V>> {
671 match self {
672 Some(value) => Set(value),
673 None => NotSet,
674 }
675 }
676}
677
678macro_rules! impl_into_active_value {
679 ($ty: ty) => {
680 impl IntoActiveValue<$ty> for $ty {
681 fn into_active_value(self) -> ActiveValue<$ty> {
682 Set(self)
683 }
684 }
685 };
686}
687
688impl_into_active_value!(bool);
689impl_into_active_value!(i8);
690impl_into_active_value!(i16);
691impl_into_active_value!(i32);
692impl_into_active_value!(i64);
693impl_into_active_value!(u8);
694impl_into_active_value!(u16);
695impl_into_active_value!(u32);
696impl_into_active_value!(u64);
697impl_into_active_value!(f32);
698impl_into_active_value!(f64);
699impl_into_active_value!(&'static str);
700impl_into_active_value!(String);
701impl_into_active_value!(Vec<u8>);
702
703#[cfg(feature = "with-json")]
704#[cfg_attr(docsrs, doc(cfg(feature = "with-json")))]
705impl_into_active_value!(crate::prelude::Json);
706
707#[cfg(feature = "with-chrono")]
708#[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
709impl_into_active_value!(crate::prelude::Date);
710
711#[cfg(feature = "with-chrono")]
712#[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
713impl_into_active_value!(crate::prelude::Time);
714
715#[cfg(feature = "with-chrono")]
716#[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
717impl_into_active_value!(crate::prelude::DateTime);
718
719#[cfg(feature = "with-chrono")]
720#[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
721impl_into_active_value!(crate::prelude::DateTimeWithTimeZone);
722
723#[cfg(feature = "with-chrono")]
724#[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
725impl_into_active_value!(crate::prelude::DateTimeUtc);
726
727#[cfg(feature = "with-chrono")]
728#[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
729impl_into_active_value!(crate::prelude::DateTimeLocal);
730
731#[cfg(feature = "with-rust_decimal")]
732#[cfg_attr(docsrs, doc(cfg(feature = "with-rust_decimal")))]
733impl_into_active_value!(crate::prelude::Decimal);
734
735#[cfg(feature = "with-uuid")]
736#[cfg_attr(docsrs, doc(cfg(feature = "with-uuid")))]
737impl_into_active_value!(crate::prelude::Uuid);
738
739#[cfg(feature = "with-time")]
740#[cfg_attr(docsrs, doc(cfg(feature = "with-time")))]
741impl_into_active_value!(crate::prelude::TimeDate);
742
743#[cfg(feature = "with-time")]
744#[cfg_attr(docsrs, doc(cfg(feature = "with-time")))]
745impl_into_active_value!(crate::prelude::TimeTime);
746
747#[cfg(feature = "with-time")]
748#[cfg_attr(docsrs, doc(cfg(feature = "with-time")))]
749impl_into_active_value!(crate::prelude::TimeDateTime);
750
751#[cfg(feature = "with-time")]
752#[cfg_attr(docsrs, doc(cfg(feature = "with-time")))]
753impl_into_active_value!(crate::prelude::TimeDateTimeWithTimeZone);
754
755#[cfg(feature = "with-ipnetwork")]
756#[cfg_attr(docsrs, doc(cfg(feature = "with-ipnetwork")))]
757impl_into_active_value!(crate::prelude::IpNetwork);
758
759impl<V> Default for ActiveValue<V>
760where
761 V: Into<Value>,
762{
763 fn default() -> Self {
765 Self::NotSet
766 }
767}
768
769impl<V> ActiveValue<V>
770where
771 V: Into<Value>,
772{
773 pub fn set(value: V) -> Self {
775 Self::Set(value)
776 }
777
778 pub fn is_set(&self) -> bool {
780 matches!(self, Self::Set(_))
781 }
782
783 pub fn unchanged(value: V) -> Self {
785 Self::Unchanged(value)
786 }
787
788 pub fn is_unchanged(&self) -> bool {
790 matches!(self, Self::Unchanged(_))
791 }
792
793 pub fn not_set() -> Self {
795 Self::default()
796 }
797
798 pub fn is_not_set(&self) -> bool {
800 matches!(self, Self::NotSet)
801 }
802
803 pub fn take(&mut self) -> Option<V> {
806 match std::mem::take(self) {
807 ActiveValue::Set(value) | ActiveValue::Unchanged(value) => Some(value),
808 ActiveValue::NotSet => None,
809 }
810 }
811
812 pub fn unwrap(self) -> V {
818 match self {
819 ActiveValue::Set(value) | ActiveValue::Unchanged(value) => value,
820 ActiveValue::NotSet => panic!("Cannot unwrap ActiveValue::NotSet"),
821 }
822 }
823
824 pub fn into_value(self) -> Option<Value> {
826 match self {
827 ActiveValue::Set(value) | ActiveValue::Unchanged(value) => Some(value.into()),
828 ActiveValue::NotSet => None,
829 }
830 }
831
832 pub fn into_wrapped_value(self) -> ActiveValue<Value> {
834 match self {
835 Self::Set(value) => ActiveValue::set(value.into()),
836 Self::Unchanged(value) => ActiveValue::unchanged(value.into()),
837 Self::NotSet => ActiveValue::not_set(),
838 }
839 }
840
841 pub fn reset(&mut self) {
844 *self = match self.take() {
845 Some(value) => ActiveValue::Set(value),
846 None => ActiveValue::NotSet,
847 };
848 }
849
850 pub fn set_if_not_equals(&mut self, value: V)
878 where
879 V: PartialEq,
880 {
881 match self {
882 ActiveValue::Unchanged(current) if &value == current => {}
883 _ => *self = ActiveValue::Set(value),
884 }
885 }
886
887 pub fn try_as_ref(&self) -> Option<&V> {
901 match self {
902 ActiveValue::Set(value) | ActiveValue::Unchanged(value) => Some(value),
903 ActiveValue::NotSet => None,
904 }
905 }
906}
907
908impl<V> std::convert::AsRef<V> for ActiveValue<V>
909where
910 V: Into<Value>,
911{
912 fn as_ref(&self) -> &V {
918 match self {
919 ActiveValue::Set(value) | ActiveValue::Unchanged(value) => value,
920 ActiveValue::NotSet => panic!("Cannot borrow ActiveValue::NotSet"),
921 }
922 }
923}
924
925impl<V> PartialEq for ActiveValue<V>
926where
927 V: Into<Value> + std::cmp::PartialEq,
928{
929 fn eq(&self, other: &Self) -> bool {
930 match (self, other) {
931 (ActiveValue::Set(l), ActiveValue::Set(r)) => l == r,
932 (ActiveValue::Unchanged(l), ActiveValue::Unchanged(r)) => l == r,
933 (ActiveValue::NotSet, ActiveValue::NotSet) => true,
934 _ => false,
935 }
936 }
937}
938
939impl<V> From<ActiveValue<V>> for ActiveValue<Option<V>>
940where
941 V: Into<Value> + Nullable,
942{
943 fn from(value: ActiveValue<V>) -> Self {
944 match value {
945 ActiveValue::Set(value) => ActiveValue::set(Some(value)),
946 ActiveValue::Unchanged(value) => ActiveValue::unchanged(Some(value)),
947 ActiveValue::NotSet => ActiveValue::not_set(),
948 }
949 }
950}
951
952#[cfg(test)]
953mod tests {
954 use crate::{entity::*, tests_cfg::*, DbErr};
955 use pretty_assertions::assert_eq;
956
957 #[cfg(feature = "with-json")]
958 use serde_json::json;
959
960 #[test]
961 #[cfg(feature = "macros")]
962 fn test_derive_into_active_model_1() {
963 mod my_fruit {
964 pub use super::fruit::*;
965 use crate as sea_orm;
966 use crate::entity::prelude::*;
967
968 #[derive(DeriveIntoActiveModel)]
969 pub struct NewFruit {
970 pub name: String,
972 pub cake_id: i32,
974 }
975 }
976
977 assert_eq!(
978 my_fruit::NewFruit {
979 name: "Apple".to_owned(),
980 cake_id: 1,
981 }
982 .into_active_model(),
983 fruit::ActiveModel {
984 id: NotSet,
985 name: Set("Apple".to_owned()),
986 cake_id: Set(Some(1)),
987 }
988 );
989 }
990
991 #[test]
992 #[cfg(feature = "macros")]
993 fn test_derive_into_active_model_2() {
994 use crate as sea_orm;
995 use crate::entity::prelude::*;
996
997 #[derive(DeriveIntoActiveModel)]
998 #[sea_orm(active_model = "fruit::ActiveModel")]
999 struct FruitName {
1000 name: String,
1001 }
1002
1003 assert_eq!(
1004 FruitName {
1005 name: "Apple Pie".to_owned(),
1006 }
1007 .into_active_model(),
1008 fruit::ActiveModel {
1009 id: NotSet,
1010 name: Set("Apple Pie".to_owned()),
1011 cake_id: NotSet,
1012 }
1013 );
1014
1015 #[derive(DeriveIntoActiveModel)]
1016 #[sea_orm(active_model = "<fruit::Entity as EntityTrait>::ActiveModel")]
1017 struct FruitCake {
1018 cake_id: Option<Option<i32>>,
1019 }
1020
1021 assert_eq!(
1022 FruitCake {
1023 cake_id: Some(Some(1)),
1024 }
1025 .into_active_model(),
1026 fruit::ActiveModel {
1027 id: NotSet,
1028 name: NotSet,
1029 cake_id: Set(Some(1)),
1030 }
1031 );
1032
1033 assert_eq!(
1034 FruitCake {
1035 cake_id: Some(None),
1036 }
1037 .into_active_model(),
1038 fruit::ActiveModel {
1039 id: NotSet,
1040 name: NotSet,
1041 cake_id: Set(None),
1042 }
1043 );
1044
1045 assert_eq!(
1046 FruitCake { cake_id: None }.into_active_model(),
1047 fruit::ActiveModel {
1048 id: NotSet,
1049 name: NotSet,
1050 cake_id: NotSet,
1051 }
1052 );
1053 }
1054
1055 #[test]
1056 #[cfg(feature = "macros")]
1057 fn test_derive_try_into_model_1() {
1058 mod my_fruit {
1059 use crate as sea_orm;
1060 use crate::entity::prelude::*;
1061
1062 #[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
1063 #[sea_orm(table_name = "fruit")]
1064 pub struct Model {
1065 #[sea_orm(primary_key)]
1066 pub id: i32,
1067 pub name: String,
1068 pub cake_id: Option<i32>,
1069 }
1070
1071 #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
1072 pub enum Relation {}
1073
1074 impl ActiveModelBehavior for ActiveModel {}
1075 }
1076 assert_eq!(
1077 my_fruit::ActiveModel {
1078 id: Set(1),
1079 name: Set("Pineapple".to_owned()),
1080 cake_id: Set(None),
1081 }
1082 .try_into_model()
1083 .unwrap(),
1084 my_fruit::Model {
1085 id: 1,
1086 name: "Pineapple".to_owned(),
1087 cake_id: None,
1088 }
1089 );
1090
1091 assert_eq!(
1092 my_fruit::ActiveModel {
1093 id: Set(2),
1094 name: Set("Apple".to_owned()),
1095 cake_id: Set(Some(1)),
1096 }
1097 .try_into_model()
1098 .unwrap(),
1099 my_fruit::Model {
1100 id: 2,
1101 name: "Apple".to_owned(),
1102 cake_id: Some(1),
1103 }
1104 );
1105
1106 assert_eq!(
1107 my_fruit::ActiveModel {
1108 id: Set(1),
1109 name: NotSet,
1110 cake_id: Set(None),
1111 }
1112 .try_into_model(),
1113 Err(DbErr::AttrNotSet(String::from("name")))
1114 );
1115
1116 assert_eq!(
1117 my_fruit::ActiveModel {
1118 id: Set(1),
1119 name: Set("Pineapple".to_owned()),
1120 cake_id: NotSet,
1121 }
1122 .try_into_model(),
1123 Err(DbErr::AttrNotSet(String::from("cake_id")))
1124 );
1125 }
1126
1127 #[test]
1128 #[cfg(feature = "macros")]
1129 fn test_derive_try_into_model_2() {
1130 mod my_fruit {
1131 use crate as sea_orm;
1132 use crate::entity::prelude::*;
1133
1134 #[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
1135 #[sea_orm(table_name = "fruit")]
1136 pub struct Model {
1137 #[sea_orm(primary_key)]
1138 pub id: i32,
1139 pub name: String,
1140 #[sea_orm(ignore)]
1141 pub cake_id: Option<i32>,
1142 }
1143
1144 #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
1145 pub enum Relation {}
1146
1147 impl ActiveModelBehavior for ActiveModel {}
1148 }
1149 assert_eq!(
1150 my_fruit::ActiveModel {
1151 id: Set(1),
1152 name: Set("Pineapple".to_owned()),
1153 }
1154 .try_into_model()
1155 .unwrap(),
1156 my_fruit::Model {
1157 id: 1,
1158 name: "Pineapple".to_owned(),
1159 cake_id: None,
1160 }
1161 );
1162 }
1163
1164 #[test]
1165 #[cfg(feature = "macros")]
1166 fn test_derive_try_into_model_3() {
1167 mod my_fruit {
1168 use crate as sea_orm;
1169 use crate::entity::prelude::*;
1170
1171 #[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
1172 #[sea_orm(table_name = "fruit")]
1173 pub struct Model {
1174 #[sea_orm(primary_key)]
1175 pub id: i32,
1176 #[sea_orm(ignore)]
1177 pub name: String,
1178 pub cake_id: Option<i32>,
1179 }
1180
1181 #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
1182 pub enum Relation {}
1183
1184 impl ActiveModelBehavior for ActiveModel {}
1185 }
1186 assert_eq!(
1187 my_fruit::ActiveModel {
1188 id: Set(1),
1189 cake_id: Set(Some(1)),
1190 }
1191 .try_into_model()
1192 .unwrap(),
1193 my_fruit::Model {
1194 id: 1,
1195 name: "".to_owned(),
1196 cake_id: Some(1),
1197 }
1198 );
1199 }
1200
1201 #[test]
1202 #[cfg(feature = "with-json")]
1203 #[should_panic(
1204 expected = r#"called `Result::unwrap()` on an `Err` value: Json("missing field `id`")"#
1205 )]
1206 fn test_active_model_set_from_json_1() {
1207 let mut cake: cake::ActiveModel = Default::default();
1208
1209 cake.set_from_json(json!({
1210 "name": "Apple Pie",
1211 }))
1212 .unwrap();
1213 }
1214
1215 #[test]
1216 #[cfg(feature = "with-json")]
1217 fn test_active_model_set_from_json_2() -> Result<(), DbErr> {
1218 let mut fruit: fruit::ActiveModel = Default::default();
1219
1220 fruit.set_from_json(json!({
1221 "name": "Apple",
1222 }))?;
1223 assert_eq!(
1224 fruit,
1225 fruit::ActiveModel {
1226 id: ActiveValue::NotSet,
1227 name: ActiveValue::Set("Apple".to_owned()),
1228 cake_id: ActiveValue::NotSet,
1229 }
1230 );
1231
1232 assert_eq!(
1233 fruit::ActiveModel::from_json(json!({
1234 "name": "Apple",
1235 }))?,
1236 fruit::ActiveModel {
1237 id: ActiveValue::NotSet,
1238 name: ActiveValue::Set("Apple".to_owned()),
1239 cake_id: ActiveValue::NotSet,
1240 }
1241 );
1242
1243 fruit.set_from_json(json!({
1244 "name": "Apple",
1245 "cake_id": null,
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(None),
1253 }
1254 );
1255
1256 fruit.set_from_json(json!({
1257 "id": null,
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 fruit.set_from_json(json!({
1271 "id": 2,
1272 "name": "Apple",
1273 "cake_id": 1,
1274 }))?;
1275 assert_eq!(
1276 fruit,
1277 fruit::ActiveModel {
1278 id: ActiveValue::NotSet,
1279 name: ActiveValue::Set("Apple".to_owned()),
1280 cake_id: ActiveValue::Set(Some(1)),
1281 }
1282 );
1283
1284 let mut fruit = fruit::ActiveModel {
1285 id: ActiveValue::Set(1),
1286 name: ActiveValue::NotSet,
1287 cake_id: ActiveValue::NotSet,
1288 };
1289 fruit.set_from_json(json!({
1290 "id": 8,
1291 "name": "Apple",
1292 "cake_id": 1,
1293 }))?;
1294 assert_eq!(
1295 fruit,
1296 fruit::ActiveModel {
1297 id: ActiveValue::Set(1),
1298 name: ActiveValue::Set("Apple".to_owned()),
1299 cake_id: ActiveValue::Set(Some(1)),
1300 }
1301 );
1302
1303 Ok(())
1304 }
1305
1306 #[smol_potat::test]
1307 #[cfg(feature = "with-json")]
1308 async fn test_active_model_set_from_json_3() -> Result<(), DbErr> {
1309 use crate::*;
1310
1311 let db = MockDatabase::new(DbBackend::Postgres)
1312 .append_exec_results([
1313 MockExecResult {
1314 last_insert_id: 1,
1315 rows_affected: 1,
1316 },
1317 MockExecResult {
1318 last_insert_id: 1,
1319 rows_affected: 1,
1320 },
1321 ])
1322 .append_query_results([
1323 [fruit::Model {
1324 id: 1,
1325 name: "Apple".to_owned(),
1326 cake_id: None,
1327 }],
1328 [fruit::Model {
1329 id: 2,
1330 name: "Orange".to_owned(),
1331 cake_id: Some(1),
1332 }],
1333 ])
1334 .into_connection();
1335
1336 let mut fruit: fruit::ActiveModel = Default::default();
1337 fruit.set_from_json(json!({
1338 "name": "Apple",
1339 }))?;
1340 fruit.save(&db).await?;
1341
1342 let mut fruit = fruit::ActiveModel {
1343 id: Set(2),
1344 ..Default::default()
1345 };
1346 fruit.set_from_json(json!({
1347 "id": 9,
1348 "name": "Orange",
1349 "cake_id": 1,
1350 }))?;
1351 fruit.save(&db).await?;
1352
1353 assert_eq!(
1354 db.into_transaction_log(),
1355 [
1356 Transaction::from_sql_and_values(
1357 DbBackend::Postgres,
1358 r#"INSERT INTO "fruit" ("name") VALUES ($1) RETURNING "id", "name", "cake_id""#,
1359 ["Apple".into()],
1360 ),
1361 Transaction::from_sql_and_values(
1362 DbBackend::Postgres,
1363 r#"UPDATE "fruit" SET "name" = $1, "cake_id" = $2 WHERE "fruit"."id" = $3 RETURNING "id", "name", "cake_id""#,
1364 ["Orange".into(), 1i32.into(), 2i32.into()],
1365 ),
1366 ]
1367 );
1368
1369 Ok(())
1370 }
1371
1372 #[test]
1373 fn test_active_model_is_changed() {
1374 let mut fruit: fruit::ActiveModel = Default::default();
1375 assert!(!fruit.is_changed());
1376
1377 fruit.set(fruit::Column::Name, "apple".into());
1378 assert!(fruit.is_changed());
1379 }
1380
1381 #[test]
1382 fn test_reset_1() {
1383 assert_eq!(
1384 fruit::Model {
1385 id: 1,
1386 name: "Apple".into(),
1387 cake_id: None,
1388 }
1389 .into_active_model(),
1390 fruit::ActiveModel {
1391 id: Unchanged(1),
1392 name: Unchanged("Apple".into()),
1393 cake_id: Unchanged(None)
1394 },
1395 );
1396
1397 assert_eq!(
1398 fruit::Model {
1399 id: 1,
1400 name: "Apple".into(),
1401 cake_id: None,
1402 }
1403 .into_active_model()
1404 .reset_all(),
1405 fruit::ActiveModel {
1406 id: Set(1),
1407 name: Set("Apple".into()),
1408 cake_id: Set(None)
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 fruit::ActiveModel {
1420 id: Unchanged(1),
1421 name: Unchanged("Apple".into()),
1422 cake_id: Unchanged(Some(2)),
1423 },
1424 );
1425
1426 assert_eq!(
1427 fruit::Model {
1428 id: 1,
1429 name: "Apple".into(),
1430 cake_id: Some(2),
1431 }
1432 .into_active_model()
1433 .reset_all(),
1434 fruit::ActiveModel {
1435 id: Set(1),
1436 name: Set("Apple".into()),
1437 cake_id: Set(Some(2)),
1438 },
1439 );
1440 }
1441
1442 #[smol_potat::test]
1443 async fn test_reset_2() -> Result<(), DbErr> {
1444 use crate::*;
1445
1446 let db = MockDatabase::new(DbBackend::Postgres)
1447 .append_exec_results(vec![
1448 MockExecResult {
1449 last_insert_id: 1,
1450 rows_affected: 1,
1451 },
1452 MockExecResult {
1453 last_insert_id: 1,
1454 rows_affected: 1,
1455 },
1456 ])
1457 .append_query_results(vec![
1458 vec![fruit::Model {
1459 id: 1,
1460 name: "Apple".to_owned(),
1461 cake_id: None,
1462 }],
1463 vec![fruit::Model {
1464 id: 1,
1465 name: "Apple".to_owned(),
1466 cake_id: None,
1467 }],
1468 ])
1469 .into_connection();
1470
1471 fruit::Model {
1472 id: 1,
1473 name: "Apple".into(),
1474 cake_id: None,
1475 }
1476 .into_active_model()
1477 .update(&db)
1478 .await?;
1479
1480 fruit::Model {
1481 id: 1,
1482 name: "Apple".into(),
1483 cake_id: None,
1484 }
1485 .into_active_model()
1486 .reset_all()
1487 .update(&db)
1488 .await?;
1489
1490 assert_eq!(
1491 db.into_transaction_log(),
1492 vec![
1493 Transaction::from_sql_and_values(
1494 DbBackend::Postgres,
1495 r#"SELECT "fruit"."id", "fruit"."name", "fruit"."cake_id" FROM "fruit" WHERE "fruit"."id" = $1 LIMIT $2"#,
1496 vec![1i32.into(), 1u64.into()],
1497 ),
1498 Transaction::from_sql_and_values(
1499 DbBackend::Postgres,
1500 r#"UPDATE "fruit" SET "name" = $1, "cake_id" = $2 WHERE "fruit"."id" = $3 RETURNING "id", "name", "cake_id""#,
1501 vec!["Apple".into(), Option::<i32>::None.into(), 1i32.into()],
1502 ),
1503 ]
1504 );
1505
1506 Ok(())
1507 }
1508}