Insert many

This commit is contained in:
Chris Tsang 2021-05-30 18:38:11 +08:00
parent ea091d7438
commit fcc668399d
4 changed files with 186 additions and 21 deletions

View File

@ -31,7 +31,7 @@ pub fn expand_derive_active_model(ident: Ident, data: Data) -> syn::Result<Token
let ty: Vec<Type> = fields.into_iter().map(|Field { ty, .. }| ty).collect(); let ty: Vec<Type> = fields.into_iter().map(|Field { ty, .. }| ty).collect();
Ok(quote!( Ok(quote!(
#[derive(Clone, Debug)] #[derive(Clone, Debug, Default)]
pub struct ActiveModel { pub struct ActiveModel {
#(pub #field: sea_orm::ActiveValue<#ty>),* #(pub #field: sea_orm::ActiveValue<#ty>),*
} }

View File

@ -10,6 +10,8 @@ where
state: ActiveValueState, state: ActiveValueState,
} }
pub type Val<V> = ActiveValue<V>;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
enum ActiveValueState { enum ActiveValueState {
Set, Set,
@ -23,6 +25,18 @@ impl Default for ActiveValueState {
} }
} }
pub trait OneOrManyActiveModel<A>
where
A: ActiveModelTrait,
{
fn is_one() -> bool;
fn get_one(self) -> A;
fn is_many() -> bool;
fn get_many(self) -> Vec<A>;
}
#[doc(hidden)]
pub fn unchanged_active_value_not_intended_for_public_use<V>(value: V) -> ActiveValue<V> pub fn unchanged_active_value_not_intended_for_public_use<V>(value: V) -> ActiveValue<V>
where where
V: Default, V: Default,
@ -30,6 +44,24 @@ where
ActiveValue::unchanged(value) ActiveValue::unchanged(value)
} }
pub trait ActiveModelOf<E>
where
E: EntityTrait,
{
}
pub trait ActiveModelTrait: Clone + Debug + Default {
type Column: ColumnTrait;
fn take(&mut self, c: Self::Column) -> ActiveValue<Value>;
fn get(&self, c: Self::Column) -> ActiveValue<Value>;
fn set(&mut self, c: Self::Column, v: Value);
fn unset(&mut self, c: Self::Column);
}
impl<V> ActiveValue<V> impl<V> ActiveValue<V>
where where
V: Default, V: Default,
@ -99,20 +131,40 @@ where
} }
} }
pub trait ActiveModelOf<E> impl<A> OneOrManyActiveModel<A> for A
where where
E: EntityTrait, A: ActiveModelTrait,
{ {
fn is_one() -> bool {
true
}
fn get_one(self) -> A {
self
}
fn is_many() -> bool {
false
}
fn get_many(self) -> Vec<A> {
panic!("not many")
}
} }
pub trait ActiveModelTrait: Clone + Debug { impl<A> OneOrManyActiveModel<A> for Vec<A>
type Column: ColumnTrait; where
A: ActiveModelTrait,
{
fn is_one() -> bool {
false
}
fn get_one(self) -> A {
panic!("not one")
}
fn take(&mut self, c: Self::Column) -> ActiveValue<Value>; fn is_many() -> bool {
true
fn get(&self, c: Self::Column) -> ActiveValue<Value>; }
fn get_many(self) -> Vec<A> {
fn set(&mut self, c: Self::Column, v: Value); self
}
fn unset(&mut self, c: Self::Column);
} }

View File

@ -1,6 +1,7 @@
use crate::{ use crate::{
ColumnTrait, ModelTrait, PrimaryKeyOfModel, PrimaryKeyTrait, QueryHelper, RelationBuilder, ActiveModelOf, ActiveModelTrait, ColumnTrait, Insert, ModelTrait, OneOrManyActiveModel,
RelationTrait, RelationType, Select, PrimaryKeyOfModel, PrimaryKeyTrait, QueryHelper, RelationBuilder, RelationTrait, RelationType,
Select,
}; };
use sea_query::{Iden, IntoValueTuple}; use sea_query::{Iden, IntoValueTuple};
use std::fmt::Debug; use std::fmt::Debug;
@ -46,7 +47,7 @@ pub trait EntityTrait: EntityName {
/// ); /// );
/// ``` /// ```
fn find() -> Select<Self> { fn find() -> Select<Self> {
Select::<Self>::new() Select::new()
} }
/// Find a model by primary key /// Find a model by primary key
@ -94,4 +95,57 @@ pub trait EntityTrait: EntityName {
} }
select select
} }
fn insert<A, C>(models: C) -> Insert<A>
where
A: ActiveModelTrait + ActiveModelOf<Self>,
C: OneOrManyActiveModel<A>,
{
if C::is_one() {
Insert::new().one(models.get_one())
} else if C::is_many() {
Insert::new().many(models.get_many())
} else {
unreachable!()
}
}
}
#[cfg(test)]
mod tests {
use crate::tests_cfg::cake;
use crate::{EntityTrait, Val};
use sea_query::PostgresQueryBuilder;
#[test]
fn insert_one() {
let apple = cake::ActiveModel {
name: Val::set("Apple Pie".to_owned()),
..Default::default()
};
assert_eq!(
cake::Entity::insert(apple)
.build(PostgresQueryBuilder)
.to_string(),
r#"INSERT INTO "cake" ("name") VALUES ('Apple Pie')"#,
);
}
#[test]
fn insert_many() {
let apple = cake::ActiveModel {
name: Val::set("Apple Pie".to_owned()),
..Default::default()
};
let orange = cake::ActiveModel {
name: Val::set("Orange Scone".to_owned()),
..Default::default()
};
assert_eq!(
cake::Entity::insert(vec![apple, orange])
.build(PostgresQueryBuilder)
.to_string(),
r#"INSERT INTO "cake" ("name") VALUES ('Apple Pie'), ('Orange Scone')"#,
);
}
} }

View File

@ -8,6 +8,7 @@ where
A: ActiveModelTrait, A: ActiveModelTrait,
{ {
pub(crate) query: InsertStatement, pub(crate) query: InsertStatement,
pub(crate) columns: Vec<bool>,
pub(crate) model: PhantomData<A>, pub(crate) model: PhantomData<A>,
} }
@ -24,6 +25,7 @@ where
query: InsertStatement::new() query: InsertStatement::new()
.into_table(E::default().into_iden()) .into_table(E::default().into_iden())
.to_owned(), .to_owned(),
columns: Vec::new(),
model: PhantomData, model: PhantomData,
} }
} }
@ -35,8 +37,14 @@ where
let mut am: A = m.into(); let mut am: A = m.into();
let mut columns = Vec::new(); let mut columns = Vec::new();
let mut values = Vec::new(); let mut values = Vec::new();
for col in A::Column::iter() { let columns_empty = self.columns.is_empty();
for (idx, col) in A::Column::iter().enumerate() {
let av = am.take(col); let av = am.take(col);
if columns_empty {
self.columns.push(av.is_set());
} else if self.columns[idx] != av.is_set() {
panic!("columns mismatch");
}
if !av.is_unset() { if !av.is_unset() {
columns.push(col); columns.push(col);
values.push(av.into_value()); values.push(av.into_value());
@ -47,6 +55,17 @@ where
self self
} }
pub fn many<M, I>(mut self, models: I) -> Self
where
M: Into<A>,
I: IntoIterator<Item = M>,
{
for model in models.into_iter() {
self = self.one(model);
}
self
}
/// Get a mutable ref to the query builder /// Get a mutable ref to the query builder
pub fn query(&mut self) -> &mut InsertStatement { pub fn query(&mut self) -> &mut InsertStatement {
&mut self.query &mut self.query
@ -74,7 +93,7 @@ where
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::tests_cfg::cake; use crate::tests_cfg::cake;
use crate::{ActiveValue, Insert}; use crate::{Insert, Val};
use sea_query::PostgresQueryBuilder; use sea_query::PostgresQueryBuilder;
#[test] #[test]
@ -82,8 +101,8 @@ mod tests {
assert_eq!( assert_eq!(
Insert::<cake::ActiveModel>::new() Insert::<cake::ActiveModel>::new()
.one(cake::ActiveModel { .one(cake::ActiveModel {
id: ActiveValue::unset(), id: Val::unset(),
name: ActiveValue::set("Apple Pie".to_owned()), name: Val::set("Apple Pie".to_owned()),
}) })
.build(PostgresQueryBuilder) .build(PostgresQueryBuilder)
.to_string(), .to_string(),
@ -96,8 +115,8 @@ mod tests {
assert_eq!( assert_eq!(
Insert::<cake::ActiveModel>::new() Insert::<cake::ActiveModel>::new()
.one(cake::ActiveModel { .one(cake::ActiveModel {
id: ActiveValue::set(1), id: Val::set(1),
name: ActiveValue::set("Apple Pie".to_owned()), name: Val::set("Apple Pie".to_owned()),
}) })
.build(PostgresQueryBuilder) .build(PostgresQueryBuilder)
.to_string(), .to_string(),
@ -118,4 +137,44 @@ mod tests {
r#"INSERT INTO "cake" ("id", "name") VALUES (1, 'Apple Pie')"#, r#"INSERT INTO "cake" ("id", "name") VALUES (1, 'Apple Pie')"#,
); );
} }
#[test]
fn insert_4() {
assert_eq!(
Insert::<cake::ActiveModel>::new()
.many(vec![
cake::Model {
id: 1,
name: "Apple Pie".to_owned(),
},
cake::Model {
id: 2,
name: "Orange Scone".to_owned(),
}
])
.build(PostgresQueryBuilder)
.to_string(),
r#"INSERT INTO "cake" ("id", "name") VALUES (1, 'Apple Pie'), (2, 'Orange Scone')"#,
);
}
#[test]
#[should_panic(expected = "columns mismatch")]
fn insert_5() {
let apple = cake::ActiveModel {
name: Val::set("Apple".to_owned()),
..Default::default()
};
let orange = cake::ActiveModel {
id: Val::set(2),
name: Val::set("Orange".to_owned()),
};
assert_eq!(
Insert::<cake::ActiveModel>::new()
.many(vec![apple, orange])
.build(PostgresQueryBuilder)
.to_string(),
r#"INSERT INTO "cake" ("id", "name") VALUES (NULL, 'Apple'), (2, 'Orange')"#,
);
}
} }