diesel::deserialize

Derive Macro QueryableByName

Source
#[derive(QueryableByName)]
{
    // Attributes available to this derive:
    #[diesel]
    #[table_name]
    #[column_name]
    #[sql_type]
}
Expand description

Implements QueryableByName for untyped sql queries, such as that one generated by sql_query

To derive this trait, Diesel needs to know the SQL type of each field. It can get the data from the corresponding table type. It uses the snake_case type name with an added s. It is possible to change this default by using #[diesel(table_name = something)]. If you define use the table type, the SQL type will be diesel::dsl::SqlTypeOf<table_name::column_name>. In cases which there are no table type, you can do the same by annotating each field with #[diesel(sql_type = SomeType)].

If the name of a field on your struct is different from the column in your table! declaration, or if you’re deriving this trait on a tuple struct, you can annotate the field with #[diesel(column_name = some_column)]. For tuple structs, all fields must have this annotation.

If a field is another struct which implements QueryableByName, instead of a column, you can annotate that with #[diesel(embed)]. Then all fields contained by that inner struct are loaded into the embedded struct.

To provide custom deserialization behavior for a field, you can use #[diesel(deserialize_as = SomeType)]. If this attribute is present, Diesel will deserialize the corresponding field into SomeType, rather than the actual field type on your struct and then call .into to convert it to the actual field type. This can be used to add custom behavior for a single field, or use types that are otherwise unsupported by Diesel.

§Attributes

§Optional container attributes

  • #[diesel(table_name = path::to::table)], to specify that this type contains columns for the specified table. The path is relative to the current module. If no field attributes are specified the derive will use the sql type of the corresponding column.
  • #[diesel(check_for_backend(diesel::pg::Pg, diesel::mysql::Mysql))], instructs the derive to generate additional code to identify potential type mismatches. It accepts a list of backend types to check the types against. Using this option will result in much better error messages in cases where some types in your QueryableByName struct don’t match. You need to specify the concrete database backend this specific struct is indented to be used with, as otherwise rustc can’t correctly identify the required deserialization implementation.

§Optional field attributes

  • #[diesel(column_name = some_column)], overrides the column name for a given field. If not set, the name of the field is used as a column name. This attribute is required on tuple structs, if #[diesel(table_name = some_table)] is used, otherwise it’s optional.
  • #[diesel(sql_type = SomeType)], assumes SomeType as sql type of the corresponding field. These attributes have precedence over all other variants to specify the sql type.
  • #[diesel(deserialize_as = Type)], instead of deserializing directly into the field type, the implementation will deserialize into Type. Then Type is converted via .into() into the field type. By default, this derive will deserialize directly into the field type
  • #[diesel(embed)], specifies that the current field maps not only a single database column, but it is a type that implements QueryableByName on its own

§Examples

If we just want to map a query to our struct, we can use derive.

#[derive(QueryableByName, PartialEq, Debug)]
struct User {
    id: i32,
    name: String,
}

let first_user = sql_query("SELECT * FROM users ORDER BY id LIMIT 1")
    .get_result(connection)?;
let expected = User { id: 1, name: "Sean".into() };
assert_eq!(expected, first_user);

If we want to do additional work during deserialization, we can use deserialize_as to use a different implementation.

struct LowercaseString(String);

impl Into<String> for LowercaseString {
    fn into(self) -> String {
        self.0
    }
}

impl<DB, ST> FromSql<ST, DB> for LowercaseString
where
    DB: Backend,
    String: FromSql<ST, DB>,
{
    fn from_sql(bytes: DB::RawValue<'_>) -> deserialize::Result<Self> {
        String::from_sql(bytes)
            .map(|s| LowercaseString(s.to_lowercase()))
    }
}

#[derive(QueryableByName, PartialEq, Debug)]
struct User {
    id: i32,
    #[diesel(deserialize_as = LowercaseString)]
    name: String,
}

let first_user = sql_query("SELECT * FROM users ORDER BY id LIMIT 1")
    .get_result(connection)?;
let expected = User { id: 1, name: "sean".into() };
assert_eq!(expected, first_user);

The custom derive generates impls similar to the following one

#[derive(PartialEq, Debug)]
struct User {
    id: i32,
    name: String,
}

impl<DB> QueryableByName<DB> for User
where
    DB: Backend,
    i32: FromSql<diesel::dsl::SqlTypeOf<users::id>, DB>,
    String: FromSql<diesel::dsl::SqlTypeOf<users::name>, DB>,
{
    fn build<'a>(row: &impl NamedRow<'a, DB>) -> deserialize::Result<Self> {
        let id = NamedRow::get::<diesel::dsl::SqlTypeOf<users::id>, _>(row, "id")?;
        let name = NamedRow::get::<diesel::dsl::SqlTypeOf<users::name>, _>(row, "name")?;

        Ok(Self { id, name })
    }
}

let first_user = sql_query("SELECT * FROM users ORDER BY id LIMIT 1")
    .get_result(connection)?;
let expected = User { id: 1, name: "Sean".into() };
assert_eq!(expected, first_user);