Handling Multiple belongs_to Relationships
When a model references the same parent model multiple times, you need to disambiguate the relationships using aliases. This guide shows how to set this up correctly.
Define the Child Model with Aliases
Use the alias attribute to give each relationship a unique name:
extern crate fabrique;
extern crate sqlx;
extern crate uuid;
use fabrique::prelude::*;
use uuid::Uuid;
#[derive(Clone, Factory, Model)]
pub struct User { id: Uuid, name: String, email: String }
#[derive(Factory, Model)]
pub struct Message {
id: Uuid,
content: String,
#[fabrique(belongs_to = "User", alias = "Sender")]
sender_id: Uuid,
#[fabrique(belongs_to = "User", alias = "Recipient")]
recipient_id: Uuid,
}
fn main() {}
This generates:
struct Senderandstruct Recipientas alias marker typesimpl BelongsTo<User, Sender> for Messageimpl BelongsTo<User, Recipient> for Messageimpl Joinable<User, Sender> for Message(and reverse)impl Joinable<User, Recipient> for Message(and reverse)
Define the Parent Model with Aliases
On the parent model, each HasMany relationship must specify which alias it
references:
extern crate fabrique;
extern crate sqlx;
extern crate uuid;
use fabrique::prelude::*;
use uuid::Uuid;
#[derive(Factory, Model)]
pub struct Message {
id: Uuid,
#[fabrique(belongs_to = "User", alias = "Sender")]
sender_id: Uuid,
#[fabrique(belongs_to = "User", alias = "Recipient")]
recipient_id: Uuid,
}
#[derive(Clone, Factory, Model)]
pub struct User {
id: Uuid,
name: String,
#[fabrique(alias = "Sender")]
sent_messages: HasMany<Message>,
#[fabrique(alias = "Recipient")]
received_messages: HasMany<Message>,
}
fn main() {}
Named Joins
With aliases, you can join the same model multiple times using join_as:
extern crate fabrique;
extern crate sqlx;
extern crate tokio;
extern crate uuid;
use fabrique::prelude::*;
use uuid::Uuid;
#[derive(Clone, Factory, Model)]
pub struct User {
id: Uuid,
name: String,
email: String
}
#[derive(Factory, Model)]
pub struct Message {
id: Uuid,
content: String,
#[fabrique(belongs_to = "User", alias = "Sender")]
sender_id: Uuid,
#[fabrique(belongs_to = "User", alias = "Recipient")]
recipient_id: Uuid,
}
#[fabrique::doctest]
async fn main(pool: Pool<Backend>) -> Result<(), fabrique::Error> {
let messages = Message::query()
.join_as::<User, Sender>()
.join_as::<User, Recipient>()
.get(&pool)
.await?;
Ok(())
}
This generates:
SELECT messages.*
FROM messages
JOIN users AS sender ON sender.id = messages.sender_id
JOIN users AS recipient ON recipient.id = messages.recipient_id
Filtering on Named Joins
Use where_on to filter on a specific alias:
extern crate fabrique;
extern crate sqlx;
extern crate tokio;
extern crate uuid;
use fabrique::prelude::*;
use uuid::Uuid;
#[derive(Clone, Factory, Model)]
pub struct User { id: Uuid, name: String, email: String }
#[derive(Factory, Model)]
pub struct Message {
id: Uuid,
content: String,
#[fabrique(belongs_to = "User", alias = "Sender")]
sender_id: Uuid,
#[fabrique(belongs_to = "User", alias = "Recipient")]
recipient_id: Uuid,
}
#[fabrique::doctest]
async fn main(pool: Pool<Backend>) -> Result<(), fabrique::Error> {
// Find messages sent by Alice
let messages = Message::query()
.join_as::<User, Sender>()
.where_on::<Sender, _, _, _, _>(User::NAME, "=", "Alice".to_string())
.get(&pool)
.await?;
Ok(())
}
Ordering by Named Joins
Use order_by_on to sort by a column from a specific alias:
extern crate fabrique;
extern crate sqlx;
extern crate tokio;
extern crate uuid;
use fabrique::prelude::*;
use uuid::Uuid;
#[derive(Clone, Factory, Model)]
pub struct User { id: Uuid, name: String, email: String }
#[derive(Factory, Model)]
pub struct Message {
id: Uuid,
content: String,
#[fabrique(belongs_to = "User", alias = "Sender")]
sender_id: Uuid,
#[fabrique(belongs_to = "User", alias = "Recipient")]
recipient_id: Uuid,
}
#[fabrique::doctest]
async fn main(pool: Pool<Backend>) -> Result<(), fabrique::Error> {
// Order messages by sender name
let messages = Message::query()
.join_as::<User, Sender>()
.order_by_on::<Sender, _, _, _>(User::NAME, "ASC")
.get(&pool)
.await?;
Ok(())
}
Using Factories
Aliases generate for_<alias> methods on the factory:
extern crate fabrique;
extern crate sqlx;
extern crate tokio;
extern crate uuid;
use fabrique::prelude::*;
use uuid::Uuid;
#[derive(Clone, Factory, Model)]
pub struct User { id: Uuid, name: String, email: String }
#[derive(Factory, Model)]
pub struct Message {
id: Uuid,
content: String,
#[fabrique(belongs_to = "User", alias = "Sender")]
sender_id: Uuid,
#[fabrique(belongs_to = "User", alias = "Recipient")]
recipient_id: Uuid,
}
#[fabrique::doctest]
async fn main(pool: Pool<Backend>) -> Result<(), fabrique::Error> {
let alice = User::factory()
.name("Alice".to_string())
.create(&pool)
.await?;
let bob = User::factory()
.name("Bob".to_string())
.create(&pool)
.await?;
let message = Message::factory()
.content("Hello Bob!".to_string())
.for_sender(alice.clone())
.for_recipient(bob)
.create(&pool)
.await?;
Ok(())
}
Using Lazy Loading
With aliases, lazy loading methods work as expected:
extern crate fabrique;
extern crate sqlx;
extern crate tokio;
extern crate uuid;
use fabrique::prelude::*;
use uuid::Uuid;
#[derive(Factory, Model)]
pub struct Message {
id: Uuid,
content: String,
#[fabrique(belongs_to = "User", alias = "Sender")]
sender_id: Uuid,
#[fabrique(belongs_to = "User", alias = "Recipient")]
recipient_id: Uuid,
}
#[derive(Clone, Factory, Model)]
pub struct User {
id: Uuid,
name: String,
email: String,
#[fabrique(alias = "Sender")]
sent_messages: HasMany<Message>,
#[fabrique(alias = "Recipient")]
received_messages: HasMany<Message>,
}
#[fabrique::doctest]
async fn main(pool: Pool<Backend>) -> Result<(), fabrique::Error> {
let user = User::factory().create(&pool).await?;
// Get messages sent by this user
let sent = user.sent_messages().get(&pool).await?;
// Get messages received by this user
let received = user.received_messages().get(&pool).await?;
Ok(())
}