Custom join on conditions (#793)
* Custom join on conditions * Try lambda approach * Implement debug for relation * Add example without `rev` * Add more examples * Docs
This commit is contained in:
parent
80d35a9d18
commit
75cb269ceb
@ -26,6 +26,7 @@ pub mod field_attr {
|
||||
pub has_many: Option<syn::Lit>,
|
||||
pub on_update: Option<syn::Lit>,
|
||||
pub on_delete: Option<syn::Lit>,
|
||||
pub on_condition: Option<syn::Lit>,
|
||||
pub from: Option<syn::Lit>,
|
||||
pub to: Option<syn::Lit>,
|
||||
pub fk_name: Option<syn::Lit>,
|
||||
|
@ -136,6 +136,17 @@ impl DeriveRelation {
|
||||
result = quote! { #result.on_delete(sea_orm::prelude::ForeignKeyAction::#on_delete) };
|
||||
}
|
||||
|
||||
if attr.on_condition.is_some() {
|
||||
let on_condition = attr
|
||||
.on_condition
|
||||
.as_ref()
|
||||
.map(Self::parse_lit_string)
|
||||
.ok_or_else(|| {
|
||||
syn::Error::new_spanned(variant, "Missing value for 'on_condition'")
|
||||
})??;
|
||||
result = quote! { #result.on_condition(|_, _| sea_orm::sea_query::IntoCondition::into_condition(#on_condition)) };
|
||||
}
|
||||
|
||||
if attr.fk_name.is_some() {
|
||||
let fk_name = attr
|
||||
.fk_name
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::{
|
||||
join_tbl_on_condition, unpack_table_ref, EntityTrait, QuerySelect, RelationDef, Select,
|
||||
};
|
||||
use sea_query::{Alias, IntoIden, JoinType, SeaRc};
|
||||
use sea_query::{Alias, Condition, IntoIden, JoinType, SeaRc};
|
||||
|
||||
/// Same as [RelationDef]
|
||||
pub type LinkDef = RelationDef;
|
||||
@ -20,20 +20,28 @@ pub trait Linked {
|
||||
/// Find all the Entities that are linked to the Entity
|
||||
fn find_linked(&self) -> Select<Self::ToEntity> {
|
||||
let mut select = Select::new();
|
||||
for (i, rel) in self.link().into_iter().rev().enumerate() {
|
||||
for (i, mut rel) in self.link().into_iter().rev().enumerate() {
|
||||
let from_tbl = Alias::new(&format!("r{}", i)).into_iden();
|
||||
let to_tbl = if i > 0 {
|
||||
Alias::new(&format!("r{}", i - 1)).into_iden()
|
||||
} else {
|
||||
unpack_table_ref(&rel.to_tbl)
|
||||
};
|
||||
let table_ref = rel.from_tbl;
|
||||
|
||||
select.query().join_as(
|
||||
JoinType::InnerJoin,
|
||||
rel.from_tbl,
|
||||
let mut condition = Condition::all().add(join_tbl_on_condition(
|
||||
SeaRc::clone(&from_tbl),
|
||||
join_tbl_on_condition(from_tbl, to_tbl, rel.from_col, rel.to_col),
|
||||
);
|
||||
SeaRc::clone(&to_tbl),
|
||||
rel.from_col,
|
||||
rel.to_col,
|
||||
));
|
||||
if let Some(f) = rel.on_condition.take() {
|
||||
condition = condition.add(f(SeaRc::clone(&from_tbl), SeaRc::clone(&to_tbl)));
|
||||
}
|
||||
|
||||
select
|
||||
.query()
|
||||
.join_as(JoinType::InnerJoin, table_ref, from_tbl, condition);
|
||||
}
|
||||
select
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::{EntityTrait, Identity, IdentityOf, Iterable, QuerySelect, Select};
|
||||
use core::marker::PhantomData;
|
||||
use sea_query::{JoinType, TableRef};
|
||||
use sea_query::{Alias, Condition, DynIden, JoinType, SeaRc, TableRef};
|
||||
use std::fmt::Debug;
|
||||
|
||||
/// Defines the type of relationship
|
||||
@ -42,7 +42,6 @@ where
|
||||
}
|
||||
|
||||
/// Defines a relationship
|
||||
#[derive(Debug)]
|
||||
pub struct RelationDef {
|
||||
/// The type of relationship defined in [RelationType]
|
||||
pub rel_type: RelationType,
|
||||
@ -62,12 +61,49 @@ pub struct RelationDef {
|
||||
/// Defines an operation to be performed on a Foreign Key when a
|
||||
/// `UPDATE` Operation is performed
|
||||
pub on_update: Option<ForeignKeyAction>,
|
||||
/// Custom join ON condition
|
||||
pub on_condition: Option<Box<dyn Fn(DynIden, DynIden) -> Condition>>,
|
||||
/// The name of foreign key constraint
|
||||
pub fk_name: Option<String>,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for RelationDef {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let mut d = f.debug_struct("RelationDef");
|
||||
d.field("rel_type", &self.rel_type)
|
||||
.field("from_tbl", &self.from_tbl)
|
||||
.field("to_tbl", &self.to_tbl)
|
||||
.field("from_col", &self.from_col)
|
||||
.field("to_col", &self.to_col)
|
||||
.field("is_owner", &self.is_owner)
|
||||
.field("on_delete", &self.on_delete)
|
||||
.field("on_update", &self.on_update);
|
||||
debug_on_condition(&mut d, &self.on_condition);
|
||||
d.field("fk_name", &self.fk_name).finish()
|
||||
}
|
||||
}
|
||||
|
||||
fn debug_on_condition(
|
||||
d: &mut core::fmt::DebugStruct<'_, '_>,
|
||||
on_condition: &Option<Box<dyn Fn(DynIden, DynIden) -> Condition>>,
|
||||
) {
|
||||
match on_condition {
|
||||
Some(func) => {
|
||||
d.field(
|
||||
"on_condition",
|
||||
&func(
|
||||
SeaRc::new(Alias::new("left")),
|
||||
SeaRc::new(Alias::new("right")),
|
||||
),
|
||||
);
|
||||
}
|
||||
None => {
|
||||
d.field("on_condition", &Option::<Condition>::None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Defines a helper to build a relation
|
||||
#[derive(Debug)]
|
||||
pub struct RelationBuilder<E, R>
|
||||
where
|
||||
E: EntityTrait,
|
||||
@ -82,9 +118,31 @@ where
|
||||
is_owner: bool,
|
||||
on_delete: Option<ForeignKeyAction>,
|
||||
on_update: Option<ForeignKeyAction>,
|
||||
on_condition: Option<Box<dyn Fn(DynIden, DynIden) -> Condition>>,
|
||||
fk_name: Option<String>,
|
||||
}
|
||||
|
||||
impl<E, R> std::fmt::Debug for RelationBuilder<E, R>
|
||||
where
|
||||
E: EntityTrait,
|
||||
R: EntityTrait,
|
||||
{
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let mut d = f.debug_struct("RelationBuilder");
|
||||
d.field("entities", &self.entities)
|
||||
.field("rel_type", &self.rel_type)
|
||||
.field("from_tbl", &self.from_tbl)
|
||||
.field("to_tbl", &self.to_tbl)
|
||||
.field("from_col", &self.from_col)
|
||||
.field("to_col", &self.to_col)
|
||||
.field("is_owner", &self.is_owner)
|
||||
.field("on_delete", &self.on_delete)
|
||||
.field("on_update", &self.on_update);
|
||||
debug_on_condition(&mut d, &self.on_condition);
|
||||
d.field("fk_name", &self.fk_name).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl RelationDef {
|
||||
/// Reverse this relation (swap from and to)
|
||||
pub fn rev(self) -> Self {
|
||||
@ -97,9 +155,46 @@ impl RelationDef {
|
||||
is_owner: !self.is_owner,
|
||||
on_delete: self.on_delete,
|
||||
on_update: self.on_update,
|
||||
on_condition: self.on_condition,
|
||||
fk_name: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Set custom join ON condition.
|
||||
///
|
||||
/// This method takes a closure with parameters
|
||||
/// denoting the left-hand side and right-hand side table in the join expression.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// cake::Entity::find()
|
||||
/// .join(
|
||||
/// JoinType::LeftJoin,
|
||||
/// cake_filling::Relation::Cake
|
||||
/// .def()
|
||||
/// .rev()
|
||||
/// .on_condition(|_left, right| {
|
||||
/// Expr::tbl(right, cake_filling::Column::CakeId)
|
||||
/// .gt(10)
|
||||
/// .into_condition()
|
||||
/// })
|
||||
/// )
|
||||
/// .build(DbBackend::MySql)
|
||||
/// .to_string(),
|
||||
/// [
|
||||
/// "SELECT `cake`.`id`, `cake`.`name` FROM `cake`",
|
||||
/// "LEFT JOIN `cake_filling` ON `cake`.`id` = `cake_filling`.`cake_id` AND `cake_filling`.`cake_id` > 10",
|
||||
/// ]
|
||||
/// .join(" ")
|
||||
/// );
|
||||
pub fn on_condition<F>(mut self, f: F) -> Self
|
||||
where
|
||||
F: Fn(DynIden, DynIden) -> Condition + 'static,
|
||||
{
|
||||
self.on_condition = Some(Box::new(f));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<E, R> RelationBuilder<E, R>
|
||||
@ -118,6 +213,7 @@ where
|
||||
is_owner,
|
||||
on_delete: None,
|
||||
on_update: None,
|
||||
on_condition: None,
|
||||
fk_name: None,
|
||||
}
|
||||
}
|
||||
@ -133,6 +229,7 @@ where
|
||||
is_owner,
|
||||
on_delete: None,
|
||||
on_update: None,
|
||||
on_condition: None,
|
||||
fk_name: None,
|
||||
}
|
||||
}
|
||||
@ -167,6 +264,18 @@ where
|
||||
self
|
||||
}
|
||||
|
||||
/// Set custom join ON condition.
|
||||
///
|
||||
/// This method takes a closure with parameters
|
||||
/// denoting the left-hand side and right-hand side table in the join expression.
|
||||
pub fn on_condition<F>(mut self, f: F) -> Self
|
||||
where
|
||||
F: Fn(DynIden, DynIden) -> Condition + 'static,
|
||||
{
|
||||
self.on_condition = Some(Box::new(f));
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the name of foreign key constraint
|
||||
pub fn fk_name(mut self, fk_name: &str) -> Self {
|
||||
self.fk_name = Some(fk_name.to_owned());
|
||||
@ -189,6 +298,7 @@ where
|
||||
is_owner: b.is_owner,
|
||||
on_delete: b.on_delete,
|
||||
on_update: b.on_update,
|
||||
on_condition: b.on_condition,
|
||||
fk_name: b.fk_name,
|
||||
}
|
||||
}
|
||||
|
@ -430,13 +430,23 @@ pub trait QueryFilter: Sized {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn join_condition(rel: RelationDef) -> SimpleExpr {
|
||||
pub(crate) fn join_condition(mut rel: RelationDef) -> Condition {
|
||||
let from_tbl = unpack_table_ref(&rel.from_tbl);
|
||||
let to_tbl = unpack_table_ref(&rel.to_tbl);
|
||||
let owner_keys = rel.from_col;
|
||||
let foreign_keys = rel.to_col;
|
||||
|
||||
join_tbl_on_condition(from_tbl, to_tbl, owner_keys, foreign_keys)
|
||||
let mut condition = Condition::all().add(join_tbl_on_condition(
|
||||
SeaRc::clone(&from_tbl),
|
||||
SeaRc::clone(&to_tbl),
|
||||
owner_keys,
|
||||
foreign_keys,
|
||||
));
|
||||
if let Some(f) = rel.on_condition.take() {
|
||||
condition = condition.add(f(from_tbl, to_tbl));
|
||||
}
|
||||
|
||||
condition
|
||||
}
|
||||
|
||||
pub(crate) fn join_tbl_on_condition(
|
||||
|
@ -3,7 +3,7 @@ use crate::{
|
||||
Linked, QuerySelect, Related, Select, SelectA, SelectB, SelectTwo, SelectTwoMany,
|
||||
};
|
||||
pub use sea_query::JoinType;
|
||||
use sea_query::{Alias, DynIden, Expr, IntoIden, SeaRc, SelectExpr};
|
||||
use sea_query::{Alias, Condition, DynIden, Expr, IntoIden, SeaRc, SelectExpr};
|
||||
|
||||
impl<E> Select<E>
|
||||
where
|
||||
@ -69,20 +69,27 @@ where
|
||||
T: EntityTrait,
|
||||
{
|
||||
let mut slf = self;
|
||||
for (i, rel) in l.link().into_iter().enumerate() {
|
||||
for (i, mut rel) in l.link().into_iter().enumerate() {
|
||||
let to_tbl = Alias::new(&format!("r{}", i)).into_iden();
|
||||
let from_tbl = if i > 0 {
|
||||
Alias::new(&format!("r{}", i - 1)).into_iden()
|
||||
} else {
|
||||
unpack_table_ref(&rel.from_tbl)
|
||||
};
|
||||
let table_ref = rel.to_tbl;
|
||||
|
||||
slf.query().join_as(
|
||||
JoinType::LeftJoin,
|
||||
rel.to_tbl,
|
||||
let mut condition = Condition::all().add(join_tbl_on_condition(
|
||||
SeaRc::clone(&from_tbl),
|
||||
SeaRc::clone(&to_tbl),
|
||||
join_tbl_on_condition(from_tbl, to_tbl, rel.from_col, rel.to_col),
|
||||
);
|
||||
rel.from_col,
|
||||
rel.to_col,
|
||||
));
|
||||
if let Some(f) = rel.on_condition.take() {
|
||||
condition = condition.add(f(SeaRc::clone(&from_tbl), SeaRc::clone(&to_tbl)));
|
||||
}
|
||||
|
||||
slf.query()
|
||||
.join_as(JoinType::LeftJoin, table_ref, to_tbl, condition);
|
||||
}
|
||||
slf = slf.apply_alias(SelectA.as_str());
|
||||
let text_type = SeaRc::new(Alias::new("text")) as DynIden;
|
||||
@ -112,8 +119,12 @@ where
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::tests_cfg::{cake, cake_filling, cake_filling_price, entity_linked, filling, fruit};
|
||||
use crate::{ColumnTrait, DbBackend, EntityTrait, ModelTrait, QueryFilter, QueryTrait};
|
||||
use crate::{
|
||||
ColumnTrait, DbBackend, EntityTrait, ModelTrait, QueryFilter, QuerySelect, QueryTrait,
|
||||
RelationTrait,
|
||||
};
|
||||
use pretty_assertions::assert_eq;
|
||||
use sea_query::{Expr, IntoCondition, JoinType};
|
||||
|
||||
#[test]
|
||||
fn join_1() {
|
||||
@ -355,4 +366,144 @@ mod tests {
|
||||
.join(" ")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn join_14() {
|
||||
assert_eq!(
|
||||
cake::Entity::find()
|
||||
.join(JoinType::LeftJoin, cake::Relation::TropicalFruit.def())
|
||||
.build(DbBackend::MySql)
|
||||
.to_string(),
|
||||
[
|
||||
"SELECT `cake`.`id`, `cake`.`name` FROM `cake`",
|
||||
"LEFT JOIN `fruit` ON `cake`.`id` = `fruit`.`cake_id` AND `fruit`.`name` LIKE '%tropical%'",
|
||||
]
|
||||
.join(" ")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn join_15() {
|
||||
let cake_model = cake::Model {
|
||||
id: 18,
|
||||
name: "".to_owned(),
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
cake_model
|
||||
.find_linked(entity_linked::CheeseCakeToFillingVendor)
|
||||
.build(DbBackend::MySql)
|
||||
.to_string(),
|
||||
[
|
||||
r#"SELECT `vendor`.`id`, `vendor`.`name`"#,
|
||||
r#"FROM `vendor`"#,
|
||||
r#"INNER JOIN `filling` AS `r0` ON `r0`.`vendor_id` = `vendor`.`id`"#,
|
||||
r#"INNER JOIN `cake_filling` AS `r1` ON `r1`.`filling_id` = `r0`.`id`"#,
|
||||
r#"INNER JOIN `cake` AS `r2` ON `r2`.`id` = `r1`.`cake_id` AND `r2`.`name` LIKE '%cheese%'"#,
|
||||
r#"WHERE `r2`.`id` = 18"#,
|
||||
]
|
||||
.join(" ")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn join_16() {
|
||||
let cake_model = cake::Model {
|
||||
id: 18,
|
||||
name: "".to_owned(),
|
||||
};
|
||||
assert_eq!(
|
||||
cake_model
|
||||
.find_linked(entity_linked::JoinWithoutReverse)
|
||||
.build(DbBackend::MySql)
|
||||
.to_string(),
|
||||
[
|
||||
r#"SELECT `vendor`.`id`, `vendor`.`name`"#,
|
||||
r#"FROM `vendor`"#,
|
||||
r#"INNER JOIN `filling` AS `r0` ON `r0`.`vendor_id` = `vendor`.`id`"#,
|
||||
r#"INNER JOIN `cake_filling` AS `r1` ON `r1`.`filling_id` = `r0`.`id`"#,
|
||||
r#"INNER JOIN `cake_filling` AS `r2` ON `r2`.`cake_id` = `r1`.`id` AND `r2`.`name` LIKE '%cheese%'"#,
|
||||
r#"WHERE `r2`.`id` = 18"#,
|
||||
]
|
||||
.join(" ")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn join_17() {
|
||||
assert_eq!(
|
||||
cake::Entity::find()
|
||||
.find_also_linked(entity_linked::CheeseCakeToFillingVendor)
|
||||
.build(DbBackend::MySql)
|
||||
.to_string(),
|
||||
[
|
||||
r#"SELECT `cake`.`id` AS `A_id`, `cake`.`name` AS `A_name`,"#,
|
||||
r#"`r2`.`id` AS `B_id`, `r2`.`name` AS `B_name`"#,
|
||||
r#"FROM `cake`"#,
|
||||
r#"LEFT JOIN `cake_filling` AS `r0` ON `cake`.`id` = `r0`.`cake_id` AND `cake`.`name` LIKE '%cheese%'"#,
|
||||
r#"LEFT JOIN `filling` AS `r1` ON `r0`.`filling_id` = `r1`.`id`"#,
|
||||
r#"LEFT JOIN `vendor` AS `r2` ON `r1`.`vendor_id` = `r2`.`id`"#,
|
||||
]
|
||||
.join(" ")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn join_18() {
|
||||
assert_eq!(
|
||||
cake::Entity::find()
|
||||
.find_also_linked(entity_linked::JoinWithoutReverse)
|
||||
.build(DbBackend::MySql)
|
||||
.to_string(),
|
||||
[
|
||||
r#"SELECT `cake`.`id` AS `A_id`, `cake`.`name` AS `A_name`,"#,
|
||||
r#"`r2`.`id` AS `B_id`, `r2`.`name` AS `B_name`"#,
|
||||
r#"FROM `cake`"#,
|
||||
r#"LEFT JOIN `cake` AS `r0` ON `cake_filling`.`cake_id` = `r0`.`id` AND `cake_filling`.`name` LIKE '%cheese%'"#,
|
||||
r#"LEFT JOIN `filling` AS `r1` ON `r0`.`filling_id` = `r1`.`id`"#,
|
||||
r#"LEFT JOIN `vendor` AS `r2` ON `r1`.`vendor_id` = `r2`.`id`"#,
|
||||
]
|
||||
.join(" ")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn join_19() {
|
||||
assert_eq!(
|
||||
cake::Entity::find()
|
||||
.join(JoinType::LeftJoin, cake::Relation::TropicalFruit.def())
|
||||
.join(
|
||||
JoinType::LeftJoin,
|
||||
cake_filling::Relation::Cake
|
||||
.def()
|
||||
.rev()
|
||||
.on_condition(|_left, right| {
|
||||
Expr::tbl(right, cake_filling::Column::CakeId)
|
||||
.gt(10)
|
||||
.into_condition()
|
||||
})
|
||||
)
|
||||
.join(
|
||||
JoinType::LeftJoin,
|
||||
cake_filling::Relation::Filling
|
||||
.def()
|
||||
.on_condition(|_left, right| {
|
||||
Expr::tbl(right, filling::Column::Name)
|
||||
.like("%lemon%")
|
||||
.into_condition()
|
||||
})
|
||||
)
|
||||
.join(JoinType::LeftJoin, filling::Relation::Vendor.def())
|
||||
.build(DbBackend::MySql)
|
||||
.to_string(),
|
||||
[
|
||||
"SELECT `cake`.`id`, `cake`.`name` FROM `cake`",
|
||||
"LEFT JOIN `fruit` ON `cake`.`id` = `fruit`.`cake_id` AND `fruit`.`name` LIKE '%tropical%'",
|
||||
"LEFT JOIN `cake_filling` ON `cake`.`id` = `cake_filling`.`cake_id` AND `cake_filling`.`cake_id` > 10",
|
||||
"LEFT JOIN `filling` ON `cake_filling`.`filling_id` = `filling`.`id` AND `filling`.`name` LIKE '%lemon%'",
|
||||
"LEFT JOIN `vendor` ON `filling`.`vendor_id` = `vendor`.`id`",
|
||||
]
|
||||
.join(" ")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,11 @@ pub struct Model {
|
||||
pub enum Relation {
|
||||
#[sea_orm(has_many = "super::fruit::Entity")]
|
||||
Fruit,
|
||||
#[sea_orm(
|
||||
has_many = "super::fruit::Entity",
|
||||
on_condition = r#"super::fruit::Column::Name.like("%tropical%")"#
|
||||
)]
|
||||
TropicalFruit,
|
||||
}
|
||||
|
||||
impl Related<super::fruit::Entity> for Entity {
|
||||
|
@ -1,4 +1,5 @@
|
||||
use crate::entity::prelude::*;
|
||||
use sea_query::{Expr, IntoCondition};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CakeToFilling;
|
||||
@ -32,3 +33,50 @@ impl Linked for CakeToFillingVendor {
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CheeseCakeToFillingVendor;
|
||||
|
||||
impl Linked for CheeseCakeToFillingVendor {
|
||||
type FromEntity = super::cake::Entity;
|
||||
|
||||
type ToEntity = super::vendor::Entity;
|
||||
|
||||
fn link(&self) -> Vec<RelationDef> {
|
||||
vec![
|
||||
super::cake_filling::Relation::Cake
|
||||
.def()
|
||||
.on_condition(|left, _right| {
|
||||
Expr::tbl(left, super::cake::Column::Name)
|
||||
.like("%cheese%")
|
||||
.into_condition()
|
||||
})
|
||||
.rev(),
|
||||
super::cake_filling::Relation::Filling.def(),
|
||||
super::filling::Relation::Vendor.def(),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct JoinWithoutReverse;
|
||||
|
||||
impl Linked for JoinWithoutReverse {
|
||||
type FromEntity = super::cake::Entity;
|
||||
|
||||
type ToEntity = super::vendor::Entity;
|
||||
|
||||
fn link(&self) -> Vec<RelationDef> {
|
||||
vec![
|
||||
super::cake_filling::Relation::Cake
|
||||
.def()
|
||||
.on_condition(|left, _right| {
|
||||
Expr::tbl(left, super::cake::Column::Name)
|
||||
.like("%cheese%")
|
||||
.into_condition()
|
||||
}),
|
||||
super::cake_filling::Relation::Filling.def(),
|
||||
super::filling::Relation::Vendor.def(),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user