[−][src]Macro juniper_eager_loading::impl_load_from_for_diesel_pg
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) } }