在柴油塔中使用新型

Posted

技术标签:

【中文标题】在柴油塔中使用新型【英文标题】:Using newtypes in diesel columns 【发布时间】:2020-04-14 02:54:02 【问题描述】:

我正在为我的柴油模型结构中的列使用struct GuildId(i64); 之类的新类型。目前我正在实现这些特征:

#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize)]
pub struct $name(pub i64);

impl AsExpression<BigInt> for $name  /* delegate to <i64 as AsExpression<BigInt> */ */ 

impl<ST, DB: Backend> Queryable<ST, DB> for $name
where i64: FromSql<ST, DB>  /* also delegate to i64 */

但是,当我尝试在以下模型结构中使用此类型时:

#[derive(Associations, Identifiable, Queryable)]
#[belongs_to(Guild)]
struct Channel 
    guild_id: GuildId,
    // other fields


#[derive(Identifiable, Queryable)]
struct Guild 
    id: GuildId,
    // other fields

Channel 仍然没有实现BelongingToDsl。当我尝试将其转换为特征时,它无法编译并显示以下消息:

error[E0277]: the trait bound `diesel::query_builder::select_statement::SelectStatement<webcord_schema::schema::channels::table>: diesel::query_dsl::filter_dsl::FilterDsl<diesel::expression::operators::Eq<webcord_schema::schema::channels::columns::guild_id, &webcord_schema::models::GuildId>>` is not satisfied
  --> src/index/guild.rs:23:32
   |
23 |                 let channels = <models::Channel as BelongingToDsl<&models::Guild>>::belonging_to(&guild)
   |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `diesel::query_dsl::filter_dsl::FilterDsl<diesel::expression::operators::Eq<webcord_schema::schema::channels::columns::guild_id, &webcord_schema::models::GuildId>>` is not implemented for `diesel::query_builder::select_statement::SelectStatement<webcord_schema::schema::channels::table>`
   |
   = help: the following implementations were found:
             <diesel::query_builder::select_statement::SelectStatement<F, S, D, W, O, L, Of, G, LC> as diesel::query_dsl::filter_dsl::FilterDsl<Predicate>>
   = note: required because of the requirements on the impl of `diesel::query_dsl::filter_dsl::FilterDsl<diesel::expression::operators::Eq<webcord_schema::schema::channels::columns::guild_id, &webcord_schema::models::GuildId>>` for `webcord_schema::schema::channels::table`

error[E0277]: the trait bound `webcord_schema::models::GuildId: diesel::expression::Expression` is not satisfied
  --> src/index/guild.rs:23:32
   |
23 |                 let channels = <models::Channel as BelongingToDsl<&models::Guild>>::belonging_to(&guild)
   |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `diesel::expression::Expression` is not implemented for `webcord_schema::models::GuildId`
   |
   = note: required because of the requirements on the impl of `diesel::expression::Expression` for `&webcord_schema::models::GuildId`
   = note: required because of the requirements on the impl of `diesel::expression::AsExpression<diesel::sql_types::BigInt>` for `&webcord_schema::models::GuildId`
   = note: required because of the requirements on the impl of `diesel::query_dsl::belonging_to_dsl::BelongingToDsl<&webcord_schema::models::Guild>` for `webcord_schema::models::Channel`

我缺少什么特质?

【问题讨论】:

BelongingToDsl trait 需要一个类型参数。我对柴油没有经验,但也许是BelongingToDiesel&lt;Guild&gt; 是的,我忘了说。但它仍然有以下要求 我更新了错误。 【参考方案1】:

该错误与BelongingToDsl 无关,而是与自定义新类型包装器的不完整实现有关。

由于错误消息表明您缺少新类型包装器的 trait impl:

error[E0277]: the trait bound `webcord_schema::models::GuildId: diesel::expression::Expression` is not satisfied
  --> src/index/guild.rs:23:32
   |
23 |                 let channels = <models::Channel as BelongingToDsl<&models::Guild>>::belonging_to(&guild)
   |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `diesel::expression::Expression` is not implemented for `webcord_schema::models::GuildId`
   |
   = note: required because of the requirements on the impl of `diesel::expression::Expression` for `&webcord_schema::models::GuildId`
   = note: required because of the requirements on the impl of `diesel::expression::AsExpression<diesel::sql_types::BigInt>` for `&webcord_schema::models::GuildId`
   = note: required because of the requirements on the impl of `diesel::query_dsl::belonging_to_dsl::BelongingToDsl<&webcord_schema::models::Guild>` for `webcord_schema::models::Channel`

有趣的是= note: required because of the requirements on the impl of diesel::expression::AsExpression&lt;diesel::sql_types::BigInt&gt; for '&amp;webcord_schema::models::GuildId 中的第二行。这意味着您至少需要添加一个 AsExpression&lt;_&gt; impl 来引用您的新类型包装器。

所以现在一般来说:this test case 展示了如何实现自定义类型。您将看到 rust 端的自定义类型使用两个自定义派生(AsExpressionFromSqlRow),它们基本上实现了您已经手动实现的特征以及缺少的特征。此外,还需要 ToSql/FromSql impl 来描述应如何将类型转换为/从 sql 类型转换。

总结一下你的类型定义应该是这样的:

#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, AsExpression, FromSqlRow)]
#[sql_type = "diesel::sql_types::BigInt")]
pub struct $name(pub i64);

impl<DB> ToSql<diesel::sql_types::BigInt, DB> for $name 
where DB: Backend,
    i64: ToSql<diesel::sql_types::BigInt, DB>,

    fn to_sql<W: Write>(&self, out: &mut Output<W, DB>) -> serialize::Result 
        <i64 as ToSql<diesel::sql_types::BigInt, DB>>::to_sql(&self.0, out)
    


impl<DB> FromSql<diesel::sql_types::BigInt, DB> for $name 
where DB: Backend,
    i64: FromSql<diesel::sql_types::BigInt, DB>

    fn from_sql(bytes: Option<&DB::RawValue>) -> deserialize::Result<Self> 
        <i64 as FromSql<diesel::sql_types::BigInt, DB>>::from_sql(bytes).map($name)
    

【讨论】:

抱歉很久没有采纳答案。我停止了那个项目,所以我还没有验证你的答案。但是感谢您的努力。 #[sql_type = "diesel::sql_types::BigInt"] 是 ToSql 和 FromSql 的 Diesel 文档页面中缺少的东西。 @M.Leonhard:这并不奇怪,因为这两个特征都与此属性无关。这是used by the AsExpression derive。 ToSql 和 FromSql 足够相关,以至于他们的文档说要添加 #[derive(AsExpression)]。但他们没有说“并且还添加这个 sql_type 属性”或“检查 AsExpression 文档以了解特殊要求”。 AsExpression 派生错误消息没有说“缺少 sql_type 属性”。因此,用户很容易错过关键信息并浪费大量时间寻找解决方案。

以上是关于在柴油塔中使用新型的主要内容,如果未能解决你的问题,请参考以下文章

如何在 graphQL 突变中使用柴油和杜松修复 mysql 的 sql 类型错误?

无法在 Rust/Actix 应用程序中使用带有柴油的计时功能

在Magento格式塔中的Rechnungen

如何使用柴油在 sqlite 中进行 upsert?

无法在锈柴油中使用通用参数实现特征

是否可以在柴油中使用没有主键的表?锈