Rework ActiveValue
(#340)
* WIP * Fixup * Fixup * Update docs & rename `unset` * Deprecate `Unset()` and reexport `ActiveValue::NotSet` * Docs Co-authored-by: Chris Tsang <chris.2y3@outlook.com>
This commit is contained in:
parent
5104cd3573
commit
adfb9ead54
@ -136,12 +136,12 @@ Fruit::update_many()
|
||||
### Save
|
||||
```rust
|
||||
let banana = fruit::ActiveModel {
|
||||
id: Unset(None),
|
||||
id: NotSet,
|
||||
name: Set("Banana".to_owned()),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
// create, because primary key `id` is `Unset`
|
||||
// create, because primary key `id` is `NotSet`
|
||||
let mut banana = banana.save(db).await?.into_active_model();
|
||||
|
||||
banana.name = Set("Banana Mongo".to_owned());
|
||||
|
@ -81,7 +81,7 @@ mod form {
|
||||
|
||||
async fn save_custom_active_model(db: &DbConn) -> Result<(), DbErr> {
|
||||
let pineapple = form::ActiveModel {
|
||||
id: Unset(None),
|
||||
id: NotSet,
|
||||
name: Set("Pineapple".to_owned()),
|
||||
};
|
||||
|
||||
|
@ -80,7 +80,7 @@ pub fn expand_derive_active_model(ident: Ident, data: Data) -> syn::Result<Token
|
||||
impl std::convert::From<<Entity as EntityTrait>::Model> for ActiveModel {
|
||||
fn from(m: <Entity as EntityTrait>::Model) -> Self {
|
||||
Self {
|
||||
#(#field: sea_orm::unchanged_active_value_not_intended_for_public_use(m.#field)),*
|
||||
#(#field: sea_orm::ActiveValue::unchanged(m.#field)),*
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -99,18 +99,18 @@ pub fn expand_derive_active_model(ident: Ident, data: Data) -> syn::Result<Token
|
||||
fn take(&mut self, c: <Self::Entity as EntityTrait>::Column) -> sea_orm::ActiveValue<sea_orm::Value> {
|
||||
match c {
|
||||
#(<Self::Entity as EntityTrait>::Column::#name => {
|
||||
let mut value = sea_orm::ActiveValue::unset();
|
||||
let mut value = sea_orm::ActiveValue::not_set();
|
||||
std::mem::swap(&mut value, &mut self.#field);
|
||||
value.into_wrapped_value()
|
||||
},)*
|
||||
_ => sea_orm::ActiveValue::unset(),
|
||||
_ => sea_orm::ActiveValue::not_set(),
|
||||
}
|
||||
}
|
||||
|
||||
fn get(&self, c: <Self::Entity as EntityTrait>::Column) -> sea_orm::ActiveValue<sea_orm::Value> {
|
||||
match c {
|
||||
#(<Self::Entity as EntityTrait>::Column::#name => self.#field.clone().into_wrapped_value(),)*
|
||||
_ => sea_orm::ActiveValue::unset(),
|
||||
_ => sea_orm::ActiveValue::not_set(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -121,23 +121,23 @@ pub fn expand_derive_active_model(ident: Ident, data: Data) -> syn::Result<Token
|
||||
}
|
||||
}
|
||||
|
||||
fn unset(&mut self, c: <Self::Entity as EntityTrait>::Column) {
|
||||
fn not_set(&mut self, c: <Self::Entity as EntityTrait>::Column) {
|
||||
match c {
|
||||
#(<Self::Entity as EntityTrait>::Column::#name => self.#field = sea_orm::ActiveValue::unset(),)*
|
||||
#(<Self::Entity as EntityTrait>::Column::#name => self.#field = sea_orm::ActiveValue::not_set(),)*
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
fn is_unset(&self, c: <Self::Entity as EntityTrait>::Column) -> bool {
|
||||
fn is_not_set(&self, c: <Self::Entity as EntityTrait>::Column) -> bool {
|
||||
match c {
|
||||
#(<Self::Entity as EntityTrait>::Column::#name => self.#field.is_unset(),)*
|
||||
#(<Self::Entity as EntityTrait>::Column::#name => self.#field.is_not_set(),)*
|
||||
_ => panic!("This ActiveModel does not have this field"),
|
||||
}
|
||||
}
|
||||
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
#(#field: sea_orm::ActiveValue::unset()),*
|
||||
#(#field: sea_orm::ActiveValue::not_set()),*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,35 +5,47 @@ use async_trait::async_trait;
|
||||
use sea_query::{Nullable, ValueTuple};
|
||||
use std::fmt::Debug;
|
||||
|
||||
/// Defines a value from an ActiveModel and its state.
|
||||
/// The field `value` takes in an [Option] type where `Option::Some(V)` , with `V` holding
|
||||
/// the value that operations like `UPDATE` are being performed on and
|
||||
/// the `state` field is either `ActiveValueState::Set` or `ActiveValueState::Unchanged`.
|
||||
/// [Option::None] in the `value` field indicates no value being performed by an operation
|
||||
/// and that the `state` field of the [ActiveValue] is set to `ActiveValueState::Unset` .
|
||||
/// #### Example snippet
|
||||
/// ```no_run
|
||||
/// // The code snipped below does an UPDATE operation on a [ActiveValue]
|
||||
/// // yielding the the SQL statement ` r#"UPDATE "fruit" SET "name" = 'Orange' WHERE "fruit"."id" = 1"# `
|
||||
pub use ActiveValue::NotSet;
|
||||
|
||||
/// Defines a stateful value used in ActiveModel.
|
||||
///
|
||||
/// There are three possible state represented by three enum variants.
|
||||
/// - [ActiveValue::Set]: A [Value] was set
|
||||
/// - [ActiveValue::Unchanged]: A [Value] remain unchanged
|
||||
/// - [ActiveValue::NotSet]: A NULL value similar to [Option::None]
|
||||
///
|
||||
/// The stateful value is useful when constructing UPDATE SQL statement,
|
||||
/// see an example below.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use sea_orm::tests_cfg::{cake, fruit};
|
||||
/// use sea_orm::{entity::*, query::*, DbBackend};
|
||||
///
|
||||
/// Update::one(fruit::ActiveModel {
|
||||
/// id: ActiveValue::set(1),
|
||||
/// name: ActiveValue::set("Orange".to_owned()),
|
||||
/// cake_id: ActiveValue::unset(),
|
||||
/// })
|
||||
/// .build(DbBackend::Postgres)
|
||||
/// .to_string();
|
||||
/// // The code snipped below does an UPDATE operation on a `ActiveValue`
|
||||
/// assert_eq!(
|
||||
/// Update::one(fruit::ActiveModel {
|
||||
/// id: ActiveValue::set(1),
|
||||
/// name: ActiveValue::set("Orange".to_owned()),
|
||||
/// cake_id: ActiveValue::not_set(),
|
||||
/// })
|
||||
/// .build(DbBackend::Postgres)
|
||||
/// .to_string(),
|
||||
/// r#"UPDATE "fruit" SET "name" = 'Orange' WHERE "fruit"."id" = 1"#
|
||||
/// );
|
||||
/// ```
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct ActiveValue<V>
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum ActiveValue<V>
|
||||
where
|
||||
V: Into<Value>,
|
||||
{
|
||||
value: Option<V>,
|
||||
state: ActiveValueState,
|
||||
/// A [Value] was set
|
||||
Set(V),
|
||||
/// A [Value] remain unchanged
|
||||
Unchanged(V),
|
||||
/// A NULL value similar to [Option::None]
|
||||
NotSet,
|
||||
}
|
||||
|
||||
/// Defines a set operation on an [ActiveValue]
|
||||
@ -45,31 +57,22 @@ where
|
||||
ActiveValue::set(v)
|
||||
}
|
||||
|
||||
/// Defines an unset operation on an [ActiveValue]
|
||||
/// Defines an not set operation on an [ActiveValue]
|
||||
#[deprecated(
|
||||
since = "0.5.0",
|
||||
note = "Please use [`ActiveValue::NotSet`] or [`NotSet`]"
|
||||
)]
|
||||
#[allow(non_snake_case)]
|
||||
pub fn Unset<V>(_: Option<bool>) -> ActiveValue<V>
|
||||
where
|
||||
V: Into<Value>,
|
||||
{
|
||||
ActiveValue::unset()
|
||||
ActiveValue::not_set()
|
||||
}
|
||||
|
||||
// Defines the state of an [ActiveValue]
|
||||
#[derive(Clone, Debug)]
|
||||
enum ActiveValueState {
|
||||
Set,
|
||||
Unchanged,
|
||||
Unset,
|
||||
}
|
||||
|
||||
impl Default for ActiveValueState {
|
||||
fn default() -> Self {
|
||||
Self::Unset
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn unchanged_active_value_not_intended_for_public_use<V>(value: V) -> ActiveValue<V>
|
||||
/// Defines an unchanged operation on an [ActiveValue]
|
||||
#[allow(non_snake_case)]
|
||||
pub fn Unchanged<V>(value: V) -> ActiveValue<V>
|
||||
where
|
||||
V: Into<Value>,
|
||||
{
|
||||
@ -93,11 +96,11 @@ pub trait ActiveModelTrait: Clone + Debug {
|
||||
/// Set the Value into an ActiveModel
|
||||
fn set(&mut self, c: <Self::Entity as EntityTrait>::Column, v: Value);
|
||||
|
||||
/// Set the state of an [ActiveValue] to the Unset state
|
||||
fn unset(&mut self, c: <Self::Entity as EntityTrait>::Column);
|
||||
/// Set the state of an [ActiveValue] to the not set state
|
||||
fn not_set(&mut self, c: <Self::Entity as EntityTrait>::Column);
|
||||
|
||||
/// Check the state of a [ActiveValue]
|
||||
fn is_unset(&self, c: <Self::Entity as EntityTrait>::Column) -> bool;
|
||||
fn is_not_set(&self, c: <Self::Entity as EntityTrait>::Column) -> bool;
|
||||
|
||||
/// The default implementation of the ActiveModel
|
||||
fn default() -> Self;
|
||||
@ -378,7 +381,7 @@ pub trait ActiveModelTrait: Clone + Debug {
|
||||
Self::after_save(model, false)
|
||||
}
|
||||
|
||||
/// Insert the model if primary key is unset, update otherwise.
|
||||
/// Insert the model if primary key is not_set, update otherwise.
|
||||
/// Only works if the entity has auto increment primary key.
|
||||
async fn save<'a, C>(self, db: &'a C) -> Result<<Self::Entity as EntityTrait>::Model, DbErr>
|
||||
where
|
||||
@ -390,7 +393,7 @@ pub trait ActiveModelTrait: Clone + Debug {
|
||||
let mut is_update = true;
|
||||
for key in <Self::Entity as EntityTrait>::PrimaryKey::iter() {
|
||||
let col = key.into_column();
|
||||
if am.is_unset(col) {
|
||||
if am.is_not_set(col) {
|
||||
is_update = false;
|
||||
break;
|
||||
}
|
||||
@ -555,7 +558,7 @@ macro_rules! impl_into_active_value {
|
||||
fn into_active_value(self) -> ActiveValue<Option<$ty>> {
|
||||
match self {
|
||||
Some(value) => Set(Some(value)),
|
||||
None => Unset(None),
|
||||
None => NotSet,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -564,7 +567,7 @@ macro_rules! impl_into_active_value {
|
||||
fn into_active_value(self) -> ActiveValue<Option<$ty>> {
|
||||
match self {
|
||||
Some(value) => Set(value),
|
||||
None => Unset(None),
|
||||
None => NotSet,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -613,74 +616,80 @@ impl_into_active_value!(crate::prelude::Decimal, Set);
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "with-uuid")))]
|
||||
impl_into_active_value!(crate::prelude::Uuid, Set);
|
||||
|
||||
impl<V> Default for ActiveValue<V>
|
||||
where
|
||||
V: Into<Value>,
|
||||
{
|
||||
fn default() -> Self {
|
||||
Self::NotSet
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> ActiveValue<V>
|
||||
where
|
||||
V: Into<Value>,
|
||||
{
|
||||
/// Set the value of an [ActiveValue] and also set its state to `ActiveValueState::Set`
|
||||
/// Create an [ActiveValue::Set]
|
||||
pub fn set(value: V) -> Self {
|
||||
Self {
|
||||
value: Some(value),
|
||||
state: ActiveValueState::Set,
|
||||
}
|
||||
Self::Set(value)
|
||||
}
|
||||
|
||||
/// Check if the state of an [ActiveValue] is `ActiveValueState::Set` which returns true
|
||||
/// Check if the [ActiveValue] is [ActiveValue::Set]
|
||||
pub fn is_set(&self) -> bool {
|
||||
matches!(self.state, ActiveValueState::Set)
|
||||
matches!(self, Self::Set(_))
|
||||
}
|
||||
|
||||
pub(crate) fn unchanged(value: V) -> Self {
|
||||
Self {
|
||||
value: Some(value),
|
||||
state: ActiveValueState::Unchanged,
|
||||
}
|
||||
/// Create an [ActiveValue::Unchanged]
|
||||
pub fn unchanged(value: V) -> Self {
|
||||
Self::Unchanged(value)
|
||||
}
|
||||
|
||||
/// Check if the status of the [ActiveValue] is `ActiveValueState::Unchanged`
|
||||
/// which returns `true` if it is
|
||||
/// Check if the [ActiveValue] is [ActiveValue::Unchanged]
|
||||
pub fn is_unchanged(&self) -> bool {
|
||||
matches!(self.state, ActiveValueState::Unchanged)
|
||||
matches!(self, Self::Unchanged(_))
|
||||
}
|
||||
|
||||
/// Set the `value` field of the ActiveModel to [Option::None] and the
|
||||
/// `state` field to `ActiveValueState::Unset`
|
||||
pub fn unset() -> Self {
|
||||
Self {
|
||||
value: None,
|
||||
state: ActiveValueState::Unset,
|
||||
/// Create an [ActiveValue::NotSet]
|
||||
pub fn not_set() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Check if the [ActiveValue] is [ActiveValue::NotSet]
|
||||
pub fn is_not_set(&self) -> bool {
|
||||
matches!(self, Self::NotSet)
|
||||
}
|
||||
|
||||
/// Get the mutable value an [ActiveValue]
|
||||
/// also setting itself to [ActiveValue::NotSet]
|
||||
pub fn take(&mut self) -> Option<V> {
|
||||
match std::mem::take(self) {
|
||||
ActiveValue::Set(value) | ActiveValue::Unchanged(value) => Some(value),
|
||||
ActiveValue::NotSet => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if the state of an [ActiveValue] is `ActiveValueState::Unset`
|
||||
/// which returns true if it is
|
||||
pub fn is_unset(&self) -> bool {
|
||||
matches!(self.state, ActiveValueState::Unset)
|
||||
}
|
||||
|
||||
/// Get the mutable value of the `value` field of an [ActiveValue]
|
||||
/// also setting it's state to `ActiveValueState::Unset`
|
||||
pub fn take(&mut self) -> Option<V> {
|
||||
self.state = ActiveValueState::Unset;
|
||||
self.value.take()
|
||||
}
|
||||
|
||||
/// Get an owned value of the `value` field of the [ActiveValue]
|
||||
/// Get an owned value of the [ActiveValue]
|
||||
pub fn unwrap(self) -> V {
|
||||
self.value.unwrap()
|
||||
match self {
|
||||
ActiveValue::Set(value) | ActiveValue::Unchanged(value) => value,
|
||||
ActiveValue::NotSet => panic!("Cannot unwrap ActiveValue::NotSet"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Check is a [Value] exists or not
|
||||
pub fn into_value(self) -> Option<Value> {
|
||||
self.value.map(Into::into)
|
||||
match self {
|
||||
ActiveValue::Set(value) | ActiveValue::Unchanged(value) => Some(value.into()),
|
||||
ActiveValue::NotSet => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrap the [Value] into a `ActiveValue<Value>`
|
||||
pub fn into_wrapped_value(self) -> ActiveValue<Value> {
|
||||
match self.state {
|
||||
ActiveValueState::Set => ActiveValue::set(self.into_value().unwrap()),
|
||||
ActiveValueState::Unchanged => ActiveValue::unchanged(self.into_value().unwrap()),
|
||||
ActiveValueState::Unset => ActiveValue::unset(),
|
||||
match self {
|
||||
Self::Set(value) => ActiveValue::set(value.into()),
|
||||
Self::Unchanged(value) => ActiveValue::unchanged(value.into()),
|
||||
Self::NotSet => ActiveValue::not_set(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -690,7 +699,10 @@ where
|
||||
V: Into<Value>,
|
||||
{
|
||||
fn as_ref(&self) -> &V {
|
||||
self.value.as_ref().unwrap()
|
||||
match self {
|
||||
ActiveValue::Set(value) | ActiveValue::Unchanged(value) => value,
|
||||
ActiveValue::NotSet => panic!("Cannot borrow ActiveValue::NotSet"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -699,7 +711,12 @@ where
|
||||
V: Into<Value> + std::cmp::PartialEq,
|
||||
{
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.value.as_ref() == other.value.as_ref()
|
||||
match (self, other) {
|
||||
(ActiveValue::Set(l), ActiveValue::Set(r)) => l == r,
|
||||
(ActiveValue::Unchanged(l), ActiveValue::Unchanged(r)) => l == r,
|
||||
(ActiveValue::NotSet, ActiveValue::NotSet) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -708,10 +725,10 @@ where
|
||||
V: Into<Value> + Nullable,
|
||||
{
|
||||
fn from(value: ActiveValue<V>) -> Self {
|
||||
match value.state {
|
||||
ActiveValueState::Set => Set(value.value),
|
||||
ActiveValueState::Unset => Unset(None),
|
||||
ActiveValueState::Unchanged => ActiveValue::unchanged(value.value),
|
||||
match value {
|
||||
ActiveValue::Set(value) => ActiveValue::set(Some(value)),
|
||||
ActiveValue::Unchanged(value) => ActiveValue::unchanged(Some(value)),
|
||||
ActiveValue::NotSet => ActiveValue::not_set(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -746,7 +763,7 @@ mod tests {
|
||||
}
|
||||
.into_active_model(),
|
||||
fruit::ActiveModel {
|
||||
id: Unset(None),
|
||||
id: NotSet,
|
||||
name: Set("Apple".to_owned()),
|
||||
cake_id: Set(Some(1)),
|
||||
}
|
||||
@ -775,8 +792,8 @@ mod tests {
|
||||
}
|
||||
.into_active_model(),
|
||||
fruit::ActiveModel {
|
||||
id: Unset(None),
|
||||
name: Unset(None),
|
||||
id: NotSet,
|
||||
name: NotSet,
|
||||
cake_id: Set(Some(1)),
|
||||
}
|
||||
);
|
||||
@ -787,8 +804,8 @@ mod tests {
|
||||
}
|
||||
.into_active_model(),
|
||||
fruit::ActiveModel {
|
||||
id: Unset(None),
|
||||
name: Unset(None),
|
||||
id: NotSet,
|
||||
name: NotSet,
|
||||
cake_id: Set(None),
|
||||
}
|
||||
);
|
||||
@ -796,9 +813,9 @@ mod tests {
|
||||
assert_eq!(
|
||||
my_fruit::UpdateFruit { cake_id: None }.into_active_model(),
|
||||
fruit::ActiveModel {
|
||||
id: Unset(None),
|
||||
name: Unset(None),
|
||||
cake_id: Unset(None),
|
||||
id: NotSet,
|
||||
name: NotSet,
|
||||
cake_id: NotSet,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -201,12 +201,12 @@
|
||||
//! # use sea_orm::{DbConn, error::*, entity::*, query::*, tests_cfg::*};
|
||||
//! # async fn function(db: &DbConn) -> Result<(), DbErr> {
|
||||
//! let banana = fruit::ActiveModel {
|
||||
//! id: Unset(None),
|
||||
//! id: NotSet,
|
||||
//! name: Set("Banana".to_owned()),
|
||||
//! ..Default::default()
|
||||
//! };
|
||||
//!
|
||||
//! // create, because primary key `id` is `Unset`
|
||||
//! // create, because primary key `id` is `NotSet`
|
||||
//! let mut banana = banana.save(db).await?.into_active_model();
|
||||
//!
|
||||
//! banana.name = Set("Banana Mongo".to_owned());
|
||||
|
@ -63,7 +63,7 @@ where
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// Insert::one(cake::ActiveModel {
|
||||
/// id: Unset(None),
|
||||
/// id: NotSet,
|
||||
/// name: Set("Apple Pie".to_owned()),
|
||||
/// })
|
||||
/// .build(DbBackend::Postgres)
|
||||
@ -190,7 +190,7 @@ mod tests {
|
||||
assert_eq!(
|
||||
Insert::<cake::ActiveModel>::new()
|
||||
.add(cake::ActiveModel {
|
||||
id: ActiveValue::unset(),
|
||||
id: ActiveValue::not_set(),
|
||||
name: ActiveValue::set("Apple Pie".to_owned()),
|
||||
})
|
||||
.build(DbBackend::Postgres)
|
||||
|
@ -236,7 +236,7 @@ mod tests {
|
||||
Update::one(fruit::ActiveModel {
|
||||
id: ActiveValue::set(1),
|
||||
name: ActiveValue::set("Orange".to_owned()),
|
||||
cake_id: ActiveValue::unset(),
|
||||
cake_id: ActiveValue::not_set(),
|
||||
})
|
||||
.build(DbBackend::Postgres)
|
||||
.to_string(),
|
||||
|
@ -53,8 +53,8 @@ async fn crud_cake(db: &DbConn) -> Result<(), DbErr> {
|
||||
assert_eq!(
|
||||
apple,
|
||||
cake::ActiveModel {
|
||||
id: Set(1),
|
||||
name: Set("Apple Pie".to_owned()),
|
||||
id: Unchanged(1),
|
||||
name: Unchanged("Apple Pie".to_owned()),
|
||||
}
|
||||
);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user