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();
Ok(quote!(
#[derive(Clone, Debug)]
#[derive(Clone, Debug, Default)]
pub struct ActiveModel {
#(pub #field: sea_orm::ActiveValue<#ty>),*
}

View File

@ -10,6 +10,8 @@ where
state: ActiveValueState,
}
pub type Val<V> = ActiveValue<V>;
#[derive(Clone, Debug)]
enum ActiveValueState {
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>
where
V: Default,
@ -30,6 +44,24 @@ where
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>
where
V: Default,
@ -99,20 +131,40 @@ where
}
}
pub trait ActiveModelOf<E>
impl<A> OneOrManyActiveModel<A> for A
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 {
type Column: ColumnTrait;
impl<A> OneOrManyActiveModel<A> for Vec<A>
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 get(&self, c: Self::Column) -> ActiveValue<Value>;
fn set(&mut self, c: Self::Column, v: Value);
fn unset(&mut self, c: Self::Column);
fn is_many() -> bool {
true
}
fn get_many(self) -> Vec<A> {
self
}
}

View File

@ -1,6 +1,7 @@
use crate::{
ColumnTrait, ModelTrait, PrimaryKeyOfModel, PrimaryKeyTrait, QueryHelper, RelationBuilder,
RelationTrait, RelationType, Select,
ActiveModelOf, ActiveModelTrait, ColumnTrait, Insert, ModelTrait, OneOrManyActiveModel,
PrimaryKeyOfModel, PrimaryKeyTrait, QueryHelper, RelationBuilder, RelationTrait, RelationType,
Select,
};
use sea_query::{Iden, IntoValueTuple};
use std::fmt::Debug;
@ -46,7 +47,7 @@ pub trait EntityTrait: EntityName {
/// );
/// ```
fn find() -> Select<Self> {
Select::<Self>::new()
Select::new()
}
/// Find a model by primary key
@ -94,4 +95,57 @@ pub trait EntityTrait: EntityName {
}
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,
{
pub(crate) query: InsertStatement,
pub(crate) columns: Vec<bool>,
pub(crate) model: PhantomData<A>,
}
@ -24,6 +25,7 @@ where
query: InsertStatement::new()
.into_table(E::default().into_iden())
.to_owned(),
columns: Vec::new(),
model: PhantomData,
}
}
@ -35,8 +37,14 @@ where
let mut am: A = m.into();
let mut columns = 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);
if columns_empty {
self.columns.push(av.is_set());
} else if self.columns[idx] != av.is_set() {
panic!("columns mismatch");
}
if !av.is_unset() {
columns.push(col);
values.push(av.into_value());
@ -47,6 +55,17 @@ where
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
pub fn query(&mut self) -> &mut InsertStatement {
&mut self.query
@ -74,7 +93,7 @@ where
#[cfg(test)]
mod tests {
use crate::tests_cfg::cake;
use crate::{ActiveValue, Insert};
use crate::{Insert, Val};
use sea_query::PostgresQueryBuilder;
#[test]
@ -82,8 +101,8 @@ mod tests {
assert_eq!(
Insert::<cake::ActiveModel>::new()
.one(cake::ActiveModel {
id: ActiveValue::unset(),
name: ActiveValue::set("Apple Pie".to_owned()),
id: Val::unset(),
name: Val::set("Apple Pie".to_owned()),
})
.build(PostgresQueryBuilder)
.to_string(),
@ -96,8 +115,8 @@ mod tests {
assert_eq!(
Insert::<cake::ActiveModel>::new()
.one(cake::ActiveModel {
id: ActiveValue::set(1),
name: ActiveValue::set("Apple Pie".to_owned()),
id: Val::set(1),
name: Val::set("Apple Pie".to_owned()),
})
.build(PostgresQueryBuilder)
.to_string(),
@ -118,4 +137,44 @@ mod tests {
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')"#,
);
}
}