macro_rules! impl_load_from_for_diesel_pg {
( $($token:tt)* ) => { ... };
}Expand description
This macro will implement LoadFrom for Diesel models using the Postgres backend.
It’ll use an = ANY which is only supported by Postgres.
§Example usage
#[macro_use]
extern crate diesel;
use diesel::pg::PgConnection;
use diesel::prelude::*;
use juniper_eager_loading::impl_load_from_for_diesel_pg;
table! {
users (id) {
id -> Integer,
}
}
table! {
companies (id) {
id -> Integer,
}
}
table! {
employments (id) {
id -> Integer,
user_id -> Integer,
company_id -> Integer,
}
}
#[derive(Queryable)]
struct User {
id: i32,
}
#[derive(Queryable)]
struct Company {
id: i32,
}
#[derive(Queryable)]
struct Employment {
id: i32,
user_id: i32,
company_id: i32,
}
struct Context {
db: PgConnection,
}
impl Context {
// The macro assumes this method exists
fn db(&self) -> &PgConnection {
&self.db
}
}
impl_load_from_for_diesel_pg! {
(
error = diesel::result::Error,
context = Context,
) => {
i32 -> (users, User),
i32 -> (companies, Company),
i32 -> (employments, Employment),
User.id -> (employments.user_id, Employment),
Company.id -> (employments.company_id, Employment),
Employment.company_id -> (companies.id, Company),
Employment.user_id -> (users.id, User),
}
}§Syntax
First you specify your error and connection type with
(
error = diesel::result::Error,
context = Context,
) => {
// ...
}Then you define each model type you want to implement LoadFrom for and which columns and
tables to use. There are two possible syntaxes for different purposes.
i32 -> (users, User),The first syntax implements LoadFrom<i32> for User, meaning from a Vec<i32> we can load a
Vec<User>. It just takes the id type, the table, and the model struct.
User.id -> (employments.user_id, Employment),This syntax is required when using HasMany and HasManyThrough. In this case it
implements LoadFrom<User> for Employment, meaning from a Vec<User> we can get
Vec<Employment>. It does this by loading the users, mapping the list to the user ids,
then finding the employments with those ids.
§Context::db
It is required that your context type has a method called db which returns a reference to a
Diesel connection that can be passed to .load(_).
Example:
struct Context {
db: PgConnection,
}
impl Context {
fn db(&self) -> &PgConnection {
&self.db
}
}
// Whatever the method returns has to work with Diesel's `load` method
users::table
.filter(users::id.eq(any(user_ids)))
.load::<User>(ctx.db())§What gets generated
The two syntaxes generates code like this:
// i32 -> (users, User),
impl juniper_eager_loading::LoadFrom<i32> for User {
type Error = diesel::result::Error;
type Context = Context;
fn load(ids: &[i32], field_args: &(), ctx: &Self::Context) -> Result<Vec<Self>, Self::Error> {
use diesel::pg::expression::dsl::any;
users::table
.filter(users::id.eq(any(ids)))
.load::<User>(ctx.db())
.map_err(From::from)
}
}
// User.id -> (employments.user_id, Employment),
impl juniper_eager_loading::LoadFrom<User> for Employment {
type Error = diesel::result::Error;
type Context = Context;
fn load(froms: &[User], field_args: &(), ctx: &Self::Context) -> Result<Vec<Self>, Self::Error> {
use diesel::pg::expression::dsl::any;
let from_ids = froms.iter().map(|other| other.id).collect::<Vec<_>>();
employments::table
.filter(employments::user_id.eq(any(from_ids)))
.load(ctx.db())
.map_err(From::from)
}
}