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
745#[cfg(feature = "with-ipnetwork")]
746#[cfg_attr(docsrs, doc(cfg(feature = "with-ipnetwork")))]
747impl_into_active_value!(crate::prelude::IpNetwork);
748
749impl<V> Default for ActiveValue<V>
750where
751 V: Into<Value>,
752{
753 fn default() -> Self {
755 Self::NotSet
756 }
757}
758
759impl<V> ActiveValue<V>
760where
761 V: Into<Value>,
762{
763 pub fn set(value: V) -> Self {
765 Self::Set(value)
766 }
767
768 pub fn is_set(&self) -> bool {
770 matches!(self, Self::Set(_))
771 }
772
773 pub fn unchanged(value: V) -> Self {
775 Self::Unchanged(value)
776 }
777
778 pub fn is_unchanged(&self) -> bool {
780 matches!(self, Self::Unchanged(_))
781 }
782
783 pub fn not_set() -> Self {
785 Self::default()
786 }
787
788 pub fn is_not_set(&self) -> bool {
790 matches!(self, Self::NotSet)
791 }
792
793 pub fn take(&mut self) -> Option<V> {
796 match std::mem::take(self) {
797 ActiveValue::Set(value) | ActiveValue::Unchanged(value) => Some(value),
798 ActiveValue::NotSet => None,
799 }
800 }
801
802 pub fn unwrap(self) -> V {
808 match self {
809 ActiveValue::Set(value) | ActiveValue::Unchanged(value) => value,
810 ActiveValue::NotSet => panic!("Cannot unwrap ActiveValue::NotSet"),
811 }
812 }
813
814 pub fn into_value(self) -> Option<Value> {
816 match self {
817 ActiveValue::Set(value) | ActiveValue::Unchanged(value) => Some(value.into()),
818 ActiveValue::NotSet => None,
819 }
820 }
821
822 pub fn into_wrapped_value(self) -> ActiveValue<Value> {
824 match self {
825 Self::Set(value) => ActiveValue::set(value.into()),
826 Self::Unchanged(value) => ActiveValue::unchanged(value.into()),
827 Self::NotSet => ActiveValue::not_set(),
828 }
829 }
830
831 pub fn reset(&mut self) {
834 *self = match self.take() {
835 Some(value) => ActiveValue::Set(value),
836 None => ActiveValue::NotSet,
837 };
838 }
839
840 pub fn set_if_not_equals(&mut self, value: V)
868 where
869 V: PartialEq,
870 {
871 match self {
872 ActiveValue::Unchanged(current) if &value == current => {}
873 _ => *self = ActiveValue::Set(value),
874 }
875 }
876
877 pub fn try_as_ref(&self) -> Option<&V> {
891 match self {
892 ActiveValue::Set(value) | ActiveValue::Unchanged(value) => Some(value),
893 ActiveValue::NotSet => None,
894 }
895 }
896}
897
898impl<V> std::convert::AsRef<V> for ActiveValue<V>
899where
900 V: Into<Value>,
901{
902 fn as_ref(&self) -> &V {
908 match self {
909 ActiveValue::Set(value) | ActiveValue::Unchanged(value) => value,
910 ActiveValue::NotSet => panic!("Cannot borrow ActiveValue::NotSet"),
911 }
912 }
913}
914
915impl<V> PartialEq for ActiveValue<V>
916where
917 V: Into<Value> + std::cmp::PartialEq,
918{
919 fn eq(&self, other: &Self) -> bool {
920 match (self, other) {
921 (ActiveValue::Set(l), ActiveValue::Set(r)) => l == r,
922 (ActiveValue::Unchanged(l), ActiveValue::Unchanged(r)) => l == r,
923 (ActiveValue::NotSet, ActiveValue::NotSet) => true,
924 _ => false,
925 }
926 }
927}
928
929impl<V> From<ActiveValue<V>> for ActiveValue<Option<V>>
930where
931 V: Into<Value> + Nullable,
932{
933 fn from(value: ActiveValue<V>) -> Self {
934 match value {
935 ActiveValue::Set(value) => ActiveValue::set(Some(value)),
936 ActiveValue::Unchanged(value) => ActiveValue::unchanged(Some(value)),
937 ActiveValue::NotSet => ActiveValue::not_set(),
938 }
939 }
940}
941
942#[cfg(test)]
943mod tests {
944 use crate::{entity::*, tests_cfg::*, DbErr};
945 use pretty_assertions::assert_eq;
946
947 #[cfg(feature = "with-json")]
948 use serde_json::json;
949
950 #[test]
951 #[cfg(feature = "macros")]
952 fn test_derive_into_active_model_1() {
953 mod my_fruit {
954 pub use super::fruit::*;
955 use crate as sea_orm;
956 use crate::entity::prelude::*;
957
958 #[derive(DeriveIntoActiveModel)]
959 pub struct NewFruit {
960 pub name: String,
962 pub cake_id: i32,
964 }
965 }
966
967 assert_eq!(
968 my_fruit::NewFruit {
969 name: "Apple".to_owned(),
970 cake_id: 1,
971 }
972 .into_active_model(),
973 fruit::ActiveModel {
974 id: NotSet,
975 name: Set("Apple".to_owned()),
976 cake_id: Set(Some(1)),
977 }
978 );
979 }
980
981 #[test]
982 #[cfg(feature = "macros")]
983 fn test_derive_into_active_model_2() {
984 use crate as sea_orm;
985 use crate::entity::prelude::*;
986
987 #[derive(DeriveIntoActiveModel)]
988 #[sea_orm(active_model = "fruit::ActiveModel")]
989 struct FruitName {
990 name: String,
991 }
992
993 assert_eq!(
994 FruitName {
995 name: "Apple Pie".to_owned(),
996 }
997 .into_active_model(),
998 fruit::ActiveModel {
999 id: NotSet,
1000 name: Set("Apple Pie".to_owned()),
1001 cake_id: NotSet,
1002 }
1003 );
1004
1005 #[derive(DeriveIntoActiveModel)]
1006 #[sea_orm(active_model = "<fruit::Entity as EntityTrait>::ActiveModel")]
1007 struct FruitCake {
1008 cake_id: Option<Option<i32>>,
1009 }
1010
1011 assert_eq!(
1012 FruitCake {
1013 cake_id: Some(Some(1)),
1014 }
1015 .into_active_model(),
1016 fruit::ActiveModel {
1017 id: NotSet,
1018 name: NotSet,
1019 cake_id: Set(Some(1)),
1020 }
1021 );
1022
1023 assert_eq!(
1024 FruitCake {
1025 cake_id: Some(None),
1026 }
1027 .into_active_model(),
1028 fruit::ActiveModel {
1029 id: NotSet,
1030 name: NotSet,
1031 cake_id: Set(None),
1032 }
1033 );
1034
1035 assert_eq!(
1036 FruitCake { cake_id: None }.into_active_model(),
1037 fruit::ActiveModel {
1038 id: NotSet,
1039 name: NotSet,
1040 cake_id: NotSet,
1041 }
1042 );
1043 }
1044
1045 #[test]
1046 #[cfg(feature = "macros")]
1047 fn test_derive_try_into_model_1() {
1048 mod my_fruit {
1049 use crate as sea_orm;
1050 use crate::entity::prelude::*;
1051
1052 #[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
1053 #[sea_orm(table_name = "fruit")]
1054 pub struct Model {
1055 #[sea_orm(primary_key)]
1056 pub id: i32,
1057 pub name: String,
1058 pub cake_id: Option<i32>,
1059 }
1060
1061 #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
1062 pub enum Relation {}
1063
1064 impl ActiveModelBehavior for ActiveModel {}
1065 }
1066 assert_eq!(
1067 my_fruit::ActiveModel {
1068 id: Set(1),
1069 name: Set("Pineapple".to_owned()),
1070 cake_id: Set(None),
1071 }
1072 .try_into_model()
1073 .unwrap(),
1074 my_fruit::Model {
1075 id: 1,
1076 name: "Pineapple".to_owned(),
1077 cake_id: None,
1078 }
1079 );
1080
1081 assert_eq!(
1082 my_fruit::ActiveModel {
1083 id: Set(2),
1084 name: Set("Apple".to_owned()),
1085 cake_id: Set(Some(1)),
1086 }
1087 .try_into_model()
1088 .unwrap(),
1089 my_fruit::Model {
1090 id: 2,
1091 name: "Apple".to_owned(),
1092 cake_id: Some(1),
1093 }
1094 );
1095
1096 assert_eq!(
1097 my_fruit::ActiveModel {
1098 id: Set(1),
1099 name: NotSet,
1100 cake_id: Set(None),
1101 }
1102 .try_into_model(),
1103 Err(DbErr::AttrNotSet(String::from("name")))
1104 );
1105
1106 assert_eq!(
1107 my_fruit::ActiveModel {
1108 id: Set(1),
1109 name: Set("Pineapple".to_owned()),
1110 cake_id: NotSet,
1111 }
1112 .try_into_model(),
1113 Err(DbErr::AttrNotSet(String::from("cake_id")))
1114 );
1115 }
1116
1117 #[test]
1118 #[cfg(feature = "macros")]
1119 fn test_derive_try_into_model_2() {
1120 mod my_fruit {
1121 use crate as sea_orm;
1122 use crate::entity::prelude::*;
1123
1124 #[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
1125 #[sea_orm(table_name = "fruit")]
1126 pub struct Model {
1127 #[sea_orm(primary_key)]
1128 pub id: i32,
1129 pub name: String,
1130 #[sea_orm(ignore)]
1131 pub cake_id: Option<i32>,
1132 }
1133
1134 #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
1135 pub enum Relation {}
1136
1137 impl ActiveModelBehavior for ActiveModel {}
1138 }
1139 assert_eq!(
1140 my_fruit::ActiveModel {
1141 id: Set(1),
1142 name: Set("Pineapple".to_owned()),
1143 }
1144 .try_into_model()
1145 .unwrap(),
1146 my_fruit::Model {
1147 id: 1,
1148 name: "Pineapple".to_owned(),
1149 cake_id: None,
1150 }
1151 );
1152 }
1153
1154 #[test]
1155 #[cfg(feature = "macros")]
1156 fn test_derive_try_into_model_3() {
1157 mod my_fruit {
1158 use crate as sea_orm;
1159 use crate::entity::prelude::*;
1160
1161 #[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
1162 #[sea_orm(table_name = "fruit")]
1163 pub struct Model {
1164 #[sea_orm(primary_key)]
1165 pub id: i32,
1166 #[sea_orm(ignore)]
1167 pub name: String,
1168 pub cake_id: Option<i32>,
1169 }
1170
1171 #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
1172 pub enum Relation {}
1173
1174 impl ActiveModelBehavior for ActiveModel {}
1175 }
1176 assert_eq!(
1177 my_fruit::ActiveModel {
1178 id: Set(1),
1179 cake_id: Set(Some(1)),
1180 }
1181 .try_into_model()
1182 .unwrap(),
1183 my_fruit::Model {
1184 id: 1,
1185 name: "".to_owned(),
1186 cake_id: Some(1),
1187 }
1188 );
1189 }
1190
1191 #[test]
1192 #[cfg(feature = "with-json")]
1193 #[should_panic(
1194 expected = r#"called `Result::unwrap()` on an `Err` value: Json("missing field `id`")"#
1195 )]
1196 fn test_active_model_set_from_json_1() {
1197 let mut cake: cake::ActiveModel = Default::default();
1198
1199 cake.set_from_json(json!({
1200 "name": "Apple Pie",
1201 }))
1202 .unwrap();
1203 }
1204
1205 #[test]
1206 #[cfg(feature = "with-json")]
1207 fn test_active_model_set_from_json_2() -> Result<(), DbErr> {
1208 let mut fruit: fruit::ActiveModel = Default::default();
1209
1210 fruit.set_from_json(json!({
1211 "name": "Apple",
1212 }))?;
1213 assert_eq!(
1214 fruit,
1215 fruit::ActiveModel {
1216 id: ActiveValue::NotSet,
1217 name: ActiveValue::Set("Apple".to_owned()),
1218 cake_id: ActiveValue::NotSet,
1219 }
1220 );
1221
1222 assert_eq!(
1223 fruit::ActiveModel::from_json(json!({
1224 "name": "Apple",
1225 }))?,
1226 fruit::ActiveModel {
1227 id: ActiveValue::NotSet,
1228 name: ActiveValue::Set("Apple".to_owned()),
1229 cake_id: ActiveValue::NotSet,
1230 }
1231 );
1232
1233 fruit.set_from_json(json!({
1234 "name": "Apple",
1235 "cake_id": null,
1236 }))?;
1237 assert_eq!(
1238 fruit,
1239 fruit::ActiveModel {
1240 id: ActiveValue::NotSet,
1241 name: ActiveValue::Set("Apple".to_owned()),
1242 cake_id: ActiveValue::Set(None),
1243 }
1244 );
1245
1246 fruit.set_from_json(json!({
1247 "id": null,
1248 "name": "Apple",
1249 "cake_id": 1,
1250 }))?;
1251 assert_eq!(
1252 fruit,
1253 fruit::ActiveModel {
1254 id: ActiveValue::NotSet,
1255 name: ActiveValue::Set("Apple".to_owned()),
1256 cake_id: ActiveValue::Set(Some(1)),
1257 }
1258 );
1259
1260 fruit.set_from_json(json!({
1261 "id": 2,
1262 "name": "Apple",
1263 "cake_id": 1,
1264 }))?;
1265 assert_eq!(
1266 fruit,
1267 fruit::ActiveModel {
1268 id: ActiveValue::NotSet,
1269 name: ActiveValue::Set("Apple".to_owned()),
1270 cake_id: ActiveValue::Set(Some(1)),
1271 }
1272 );
1273
1274 let mut fruit = fruit::ActiveModel {
1275 id: ActiveValue::Set(1),
1276 name: ActiveValue::NotSet,
1277 cake_id: ActiveValue::NotSet,
1278 };
1279 fruit.set_from_json(json!({
1280 "id": 8,
1281 "name": "Apple",
1282 "cake_id": 1,
1283 }))?;
1284 assert_eq!(
1285 fruit,
1286 fruit::ActiveModel {
1287 id: ActiveValue::Set(1),
1288 name: ActiveValue::Set("Apple".to_owned()),
1289 cake_id: ActiveValue::Set(Some(1)),
1290 }
1291 );
1292
1293 Ok(())
1294 }
1295
1296 #[smol_potat::test]
1297 #[cfg(feature = "with-json")]
1298 async fn test_active_model_set_from_json_3() -> Result<(), DbErr> {
1299 use crate::*;
1300
1301 let db = MockDatabase::new(DbBackend::Postgres)
1302 .append_exec_results([
1303 MockExecResult {
1304 last_insert_id: 1,
1305 rows_affected: 1,
1306 },
1307 MockExecResult {
1308 last_insert_id: 1,
1309 rows_affected: 1,
1310 },
1311 ])
1312 .append_query_results([
1313 [fruit::Model {
1314 id: 1,
1315 name: "Apple".to_owned(),
1316 cake_id: None,
1317 }],
1318 [fruit::Model {
1319 id: 2,
1320 name: "Orange".to_owned(),
1321 cake_id: Some(1),
1322 }],
1323 ])
1324 .into_connection();
1325
1326 let mut fruit: fruit::ActiveModel = Default::default();
1327 fruit.set_from_json(json!({
1328 "name": "Apple",
1329 }))?;
1330 fruit.save(&db).await?;
1331
1332 let mut fruit = fruit::ActiveModel {
1333 id: Set(2),
1334 ..Default::default()
1335 };
1336 fruit.set_from_json(json!({
1337 "id": 9,
1338 "name": "Orange",
1339 "cake_id": 1,
1340 }))?;
1341 fruit.save(&db).await?;
1342
1343 assert_eq!(
1344 db.into_transaction_log(),
1345 [
1346 Transaction::from_sql_and_values(
1347 DbBackend::Postgres,
1348 r#"INSERT INTO "fruit" ("name") VALUES ($1) RETURNING "id", "name", "cake_id""#,
1349 ["Apple".into()],
1350 ),
1351 Transaction::from_sql_and_values(
1352 DbBackend::Postgres,
1353 r#"UPDATE "fruit" SET "name" = $1, "cake_id" = $2 WHERE "fruit"."id" = $3 RETURNING "id", "name", "cake_id""#,
1354 ["Orange".into(), 1i32.into(), 2i32.into()],
1355 ),
1356 ]
1357 );
1358
1359 Ok(())
1360 }
1361
1362 #[test]
1363 fn test_active_model_is_changed() {
1364 let mut fruit: fruit::ActiveModel = Default::default();
1365 assert!(!fruit.is_changed());
1366
1367 fruit.set(fruit::Column::Name, "apple".into());
1368 assert!(fruit.is_changed());
1369 }
1370
1371 #[test]
1372 fn test_reset_1() {
1373 assert_eq!(
1374 fruit::Model {
1375 id: 1,
1376 name: "Apple".into(),
1377 cake_id: None,
1378 }
1379 .into_active_model(),
1380 fruit::ActiveModel {
1381 id: Unchanged(1),
1382 name: Unchanged("Apple".into()),
1383 cake_id: Unchanged(None)
1384 },
1385 );
1386
1387 assert_eq!(
1388 fruit::Model {
1389 id: 1,
1390 name: "Apple".into(),
1391 cake_id: None,
1392 }
1393 .into_active_model()
1394 .reset_all(),
1395 fruit::ActiveModel {
1396 id: Set(1),
1397 name: Set("Apple".into()),
1398 cake_id: Set(None)
1399 },
1400 );
1401
1402 assert_eq!(
1403 fruit::Model {
1404 id: 1,
1405 name: "Apple".into(),
1406 cake_id: Some(2),
1407 }
1408 .into_active_model(),
1409 fruit::ActiveModel {
1410 id: Unchanged(1),
1411 name: Unchanged("Apple".into()),
1412 cake_id: Unchanged(Some(2)),
1413 },
1414 );
1415
1416 assert_eq!(
1417 fruit::Model {
1418 id: 1,
1419 name: "Apple".into(),
1420 cake_id: Some(2),
1421 }
1422 .into_active_model()
1423 .reset_all(),
1424 fruit::ActiveModel {
1425 id: Set(1),
1426 name: Set("Apple".into()),
1427 cake_id: Set(Some(2)),
1428 },
1429 );
1430 }
1431
1432 #[smol_potat::test]
1433 async fn test_reset_2() -> Result<(), DbErr> {
1434 use crate::*;
1435
1436 let db = MockDatabase::new(DbBackend::Postgres)
1437 .append_exec_results(vec![
1438 MockExecResult {
1439 last_insert_id: 1,
1440 rows_affected: 1,
1441 },
1442 MockExecResult {
1443 last_insert_id: 1,
1444 rows_affected: 1,
1445 },
1446 ])
1447 .append_query_results(vec![
1448 vec![fruit::Model {
1449 id: 1,
1450 name: "Apple".to_owned(),
1451 cake_id: None,
1452 }],
1453 vec![fruit::Model {
1454 id: 1,
1455 name: "Apple".to_owned(),
1456 cake_id: None,
1457 }],
1458 ])
1459 .into_connection();
1460
1461 fruit::Model {
1462 id: 1,
1463 name: "Apple".into(),
1464 cake_id: None,
1465 }
1466 .into_active_model()
1467 .update(&db)
1468 .await?;
1469
1470 fruit::Model {
1471 id: 1,
1472 name: "Apple".into(),
1473 cake_id: None,
1474 }
1475 .into_active_model()
1476 .reset_all()
1477 .update(&db)
1478 .await?;
1479
1480 assert_eq!(
1481 db.into_transaction_log(),
1482 vec![
1483 Transaction::from_sql_and_values(
1484 DbBackend::Postgres,
1485 r#"SELECT "fruit"."id", "fruit"."name", "fruit"."cake_id" FROM "fruit" WHERE "fruit"."id" = $1 LIMIT $2"#,
1486 vec![1i32.into(), 1u64.into()],
1487 ),
1488 Transaction::from_sql_and_values(
1489 DbBackend::Postgres,
1490 r#"UPDATE "fruit" SET "name" = $1, "cake_id" = $2 WHERE "fruit"."id" = $3 RETURNING "id", "name", "cake_id""#,
1491 vec!["Apple".into(), Option::<i32>::None.into(), 1i32.into()],
1492 ),
1493 ]
1494 );
1495
1496 Ok(())
1497 }
1498}