Insert many
This commit is contained in:
parent
ea091d7438
commit
fcc668399d
@ -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>),*
|
||||||
}
|
}
|
||||||
|
@ -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);
|
|
||||||
}
|
}
|
||||||
|
@ -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')"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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')"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user