Writing Diesel CRUD operations for generic types

别说谁变了你拦得住时间么 提交于 2021-02-18 17:34:37

问题


I am trying to write a Rust crate which removes some boilerplate code from the user when creating simple CRUD operations with Diesel

For instance, if you have a Diesel Insertable like this one:

#[derive(Insertable)]
#[table_name = "users"]
pub struct UserCreate<'a> {
    pub email: String,
    pub hash: &'a [u8],
    pub first_name: Option<String>,
    pub family_name: Option<String>,
}

I want the crate user to just write create<UserCreate>(model, pool), to insert the struct fields into a database row.

To do so, I've written the following function signature (simplified for example):

fn create<'a, C: 'a>(model: C, pool: DBPool)
where
    C: diesel::Identifiable,
    &'a C: diesel::Insertable<C::Table>,
{
    let conn = pool.get().unwrap();
    diesel::insert_into(C::table())
        .values(&model)
        .execute(&conn);
}

The issue is that the compiler complains about some missing trait bounds for C and &C at .execute(&conn) and I am not quite sure how to place them in the where clause, there may be also a simpler way of doing this which I am not aware of. Any hint is very welcome!

Compiler output:

error[E0277]: the trait bound `<<C as diesel::associations::HasTable>::Table as diesel::QuerySource>::FromClause: diesel::query_builder::QueryFragment<_>` is not satisfied
  --> database/src/users/models.rs:46:10
   |
46 |         .execute(&conn);
   |          ^^^^^^^ the trait `diesel::query_builder::QueryFragment<_>` is not implemented for `<<C as diesel::associations::HasTable>::Table as diesel::QuerySource>::FromClause`
   |
   = help: the following implementations were found:
             <&'a T as diesel::query_builder::QueryFragment<DB>>
   = note: required because of the requirements on the impl of `diesel::query_builder::QueryFragment<_>` for `diesel::query_builder::InsertStatement<<C as diesel::associations::HasTable>::Table, <&C as diesel::Insertable<<C as diesel::associations::HasTable>::Table>>::Values>`
   = note: required because of the requirements on the impl of `diesel::query_dsl::load_dsl::ExecuteDsl<_, _>` for `diesel::query_builder::InsertStatement<<C as diesel::associations::HasTable>::Table, <&C as diesel::Insertable<<C as diesel::associations::HasTable>::Table>>::Values>`

error[E0277]: the trait bound `<&C as diesel::Insertable<<C as diesel::associations::HasTable>::Table>>::Values: diesel::query_builder::QueryFragment<_>` is not satisfied
  --> database/src/users/models.rs:46:10
   |
46 |         .execute(&conn);
   |          ^^^^^^^ the trait `diesel::query_builder::QueryFragment<_>` is not implemented for `<&C as diesel::Insertable<<C as diesel::associations::HasTable>::Table>>::Values`
   |
   = help: the following implementations were found:
             <&'a T as diesel::query_builder::QueryFragment<DB>>
   = note: required because of the requirements on the impl of `diesel::query_builder::QueryFragment<_>` for `diesel::query_builder::InsertStatement<<C as diesel::associations::HasTable>::Table, <&C as diesel::Insertable<<C as diesel::associations::HasTable>::Table>>::Values>`
   = note: required because of the requirements on the impl of `diesel::query_dsl::load_dsl::ExecuteDsl<_, _>` for `diesel::query_builder::InsertStatement<<C as diesel::associations::HasTable>::Table, <&C as diesel::Insertable<<C as diesel::associations::HasTable>::Table>>::Values>`

error[E0277]: the trait bound `<&C as diesel::Insertable<<C as diesel::associations::HasTable>::Table>>::Values: diesel::insertable::CanInsertInSingleQuery<_>` is not satisfied
  --> database/src/users/models.rs:46:10
   |
46 |         .execute(&conn);
   |          ^^^^^^^ the trait `diesel::insertable::CanInsertInSingleQuery<_>` is not implemented for `<&C as diesel::Insertable<<C as diesel::associations::HasTable>::Table>>::Values`
   |
   = help: the following implementations were found:
             <&'a T as diesel::insertable::CanInsertInSingleQuery<DB>>
   = note: required because of the requirements on the impl of `diesel::query_builder::QueryFragment<_>` for `diesel::query_builder::InsertStatement<<C as diesel::associations::HasTable>::Table, <&C as diesel::Insertable<<C as diesel::associations::HasTable>::Table>>::Values>`
   = note: required because of the requirements on the impl of `diesel::query_dsl::load_dsl::ExecuteDsl<_, _>` for `diesel::query_builder::InsertStatement<<C as diesel::associations::HasTable>::Table, <&C as diesel::Insertable<<C as diesel::associations::HasTable>::Table>>::Values>`

error: aborting due to 3 previous errors

Thank you very much!


回答1:


I've finally solved by defining the following trait bounds!

fn create<C, T>(model: C, pool: DBPool)
where
    T: diesel::associations::HasTable,
    <T::Table as diesel::QuerySource>::FromClause:
        diesel::query_builder::QueryFragment<diesel::pg::Pg>,
    C: diesel::Insertable<T::Table>,
    C::Values: diesel::insertable::CanInsertInSingleQuery<diesel::pg::Pg>
        + diesel::query_builder::QueryFragment<diesel::pg::Pg>,
{
    let conn = pool.get().unwrap();

    diesel::insert_into(T::table())
        .values(model)
        .execute(&conn);
}

create::<UserCreate, users::table>(user, pool);

Basically, you need a pair of extra bounds for the Table and the Insertable. It would be nice if it was possible to get the table directly from the Insertable, to avoid using another type in the function definition, but I can work with that :)




回答2:


The accepted answer is correct but can be made less verbose by using higher bounds

use diesel::query_builder::{InsertStatement};
use diesel::query_dsl::methods::{ExecuteDsl};

pub fn insert_into_table<T, M>(conn: &Pgconnection, table: T, records: M)
where
    T: Table,
    M: diesel::Insertable<T>,
    InsertStatement<T, M::Values>: ExecuteDsl<PgConnection>,
{
    diesel::insert_into(table)
        .values(records)
        .execute(conn);
}


来源:https://stackoverflow.com/questions/58589018/writing-diesel-crud-operations-for-generic-types

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!