Rust Diesel Abstract update function

Viewed 315

I currently trying to implement abstract function which will update a few meta fields for any table in the database, but getting problems with Identifiable.

I have a database where every table has meta-fields:

....
pub updu: Option<Uuid>, // ID of a user who changed it 
pub updt: Option<NaiveDateTime>, //updated with current date/time on every change
pub ver: Option<i32>, //Version increases on every change
.....

I want to implement a function which would make update for every entity. Currently I have this implementation:

pub fn update<Model>(
    conn: &PgConnection,
    old_model: Model,
    mut updated_model: Model,
    user_id: Uuid,
) -> Result<Model, diesel::result::Error>
where
    Model: MetaFields + AsChangeset<Target = <Model as HasTable>::Table> + IntoUpdateTarget,
    Update<Model, Model>: LoadQuery<PgConnection, Model>
{
    updated_model.update_fields(user_id);
    Ok(
        diesel::update(old_model)
            .set(updated_model)
            .get_result(conn).unwrap()
    )

When I am trying to call it it shows this error:

error[E0277]: the trait bound `Marking: Identifiable` is not satisfied
   --> src/service/marking_service.rs:116:24
    |
116 |     web::block(move || common::dao::update(&conn2, real_marking1[0].clone(), marking2_to_update, jwt_token.user_id))
    |                        ^^^^^^^^^^^^^^^^^^^ the trait `Identifiable` is not implemented for `Marking`
    |
    = help: the following implementations were found:
              <&'ident Marking as Identifiable>
    = note: required because of the requirements on the impl of `IntoUpdateTarget` for `Marking`

For more information about this error, try `rustc --explain E0277`.
error: could not compile `testapi` due to previous error

An entity which I am trying to update in this example is:

use chrono::{NaiveDateTime, Utc};
use common::model::MetaFields;
use common::utils::constants::DEL_MARK_AVAILABLE;
use serde::{Deserialize, Serialize};
use serde_json;
use uuid::Uuid;

use crate::schema::marking;

#[derive(
    Clone,
    Serialize,
    Deserialize,
    Debug,
    Queryable,
    Insertable,
    AsChangeset,
    Identifiable,
    QueryableByName,
    Default,
)]
#[primary_key(uuid)]
#[table_name = "marking"]
pub struct Marking {
    pub uuid: Uuid,
    pub updt: Option<NaiveDateTime>,
    pub ver: Option<i32>,
    pub updu: Option<Uuid>,
    pub comment: Option<String>,
    pub status: Option<String>,
}

impl MetaFields for Marking {
    fn update_fields(&mut self, user_id: Uuid) {
        self.updu = Option::from(user_id);
        self.ver = Option::from(self.ver.unwrap() + 1);
        self.updt = Option::from(Utc::now().naive_local());
    }
}

As you can see Identifiable is defined for this entity, but for some reason update cannot see it. Could someone suggest what I am missing here? Update, schema:

table! {
    marking (uuid) {
        uuid -> Uuid,
        updt -> Nullable<Timestamp>,
        ver -> Nullable<Int4>,
        updu -> Nullable<Uuid>,
        comment -> Nullable<Varchar>,
        status -> Nullable<Varchar>,
    }
}
diesel = { version = "1.4.6", features = ["postgres", "uuid", "chrono", "uuidv07", "serde_json"] }
r2d2 = "0.8"
r2d2-diesel = "1.0.0"
diesel_json = "0.1.0"
1 Answers

Your code is almost correct. The error message already mentions the issue:

#[derive(Identifiable)] does generate basically the following impl: impl<'a> Identifiable for &'a struct {}, which means that trait is only implemented for a reference to self. Depending on your other trait setup you can try the following things:

  • Pass a reference as old_model common::dao::update
  • Change the definition of common::dao::update to take a reference as second argument. Then you can separate the trait bounds for Model so that you bound IntoUpdateTarget on &Model instead.

(It's hard to guess which one will be the better solution as your question is missing a lot of important context. Please try to provide a complete minimal example in the future.)

Related