【问题标题】:Multiple foreign keys referencing same table in Diesel在 Diesel 中引用同一个表的多个外键
【发布时间】:2021-04-07 09:46:11
【问题描述】:

我正在尝试创建一个引用同一个表两次的结构。这样做的目的是创建一种类别的层次结构。这是我要为以下表格做的事情:

create table product_category_rollup(
    id serial primary key,
    upper_category_id integer not null,
    lower_category_id integer not null,
    foreign key (upper_category_id) references product_category(id),
    foreign key (lower_category_id) references product_category(id)
);

create table product_category(
    id serial primary key,
    name varchar unique not null
);

我正在尝试创建匹配的结构:

#[derive(Identifiable, Queryable)]
#[table_name = "product_category"]
pub struct ProductCategory {
    id: i32,
    name: String,
}

#[derive(Queryable, Identifiable, Associations)]
#[belongs_to(ProductCategory, foreign_key="upper_category_id")]
#[belongs_to(ProductCategory, foreign_key="lower_category_id")]
#[table_name = "product_category_rollup"]
pub struct ProductCategoryRollup {
    id: i32,
    upper_category_id: i32,
    lower_category_id: i32,
}

我收到一条错误消息:

error[E0119]: conflicting implementations of trait `diesel::associations::BelongsTo<entities::ProductCategory>` for type `entities::ProductCategoryRollup`:
  --> src/entities.rs:29:35
   |
29 | #[derive(Queryable, Identifiable, Associations)]
   |                                   ^^^^^^^^^^^^
   |                                   |
   |                                   first implementation here
   |                                   conflicting implementation for `entities::ProductCategoryRollup`
   |
   = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)

让多个外键引用同一个表的正确方法是什么?这是 Diesel 中尚未解决的一些固有限制吗?

【问题讨论】:

    标签: rust rust-diesel


    【解决方案1】:

    BelongsTo 特征定义为:

    pub trait BelongsTo<Parent> {
        type ForeignKey: Hash + Eq;
        type ForeignKeyColumn: Column;
        fn foreign_key(&self) -> Option<&Self::ForeignKey>;
        fn foreign_key_column() -> Self::ForeignKeyColumn;
    }
    

    由于ForeignKey(和ForeignKeyColumn)是关联类型,而不是泛型参数,给定的Child只能有BelongsTo&lt;Parent&gt;的一个实现。


    总的来说,BelongsTo 似乎相当有限;请注意,它也仅限于单列。

    【讨论】:

    • 这对我来说当然很清楚,但这并不能回答我如何实现我想要实现的目标。
    • 对不起;我陷入了探索,忘记了你问题的那一部分。老实说,我不确定这是否可能。 Diesel 开发人员在 r/rust (Reddit) 上闲逛,所以我建议你在那里问你的问题。如果你这样做了,请交叉引用堆栈溢出问题,以便如果有人有解决方法,他们也可以在这里回答。
    • 我已经在 Github 上提交了一个 issue 链接到这里,所以我们将不得不拭目以待。
    • @TheCoolDrop:祝你好运,祝你新年快乐(可能提前)。
    【解决方案2】:

    所以我一直在研究和研究 Diesel,正如上面的答案中已经指出的那样,这个问题的出现是由于 BelongsTo&lt;Parent&gt; 特征的定义方式。

    避免这种情况的一种方法是执行以下操作:

    // This trait contains the behavior common to all types
    // representing the product category
    trait ProductCategory{
        fn new(id: i32, name: String) -> Self;
    }
    
    #[derive(Identifiable)]
    #[table_name = "product_category"]
    pub struct RawProductCategory {
        id: i32,
        name: String,
    }
    
    #[derive(Identifiable)]
    #[table_name = "product_category"]
    pub struct UpperProductCategory {
        pub id: i32,
        pub name: String,
    }
    
    #[derive(Identifiable)]
    #[table_name = "product_category"]
    pub struct LowerProductCategory {
        pub id: i32,
        pub name: String
    }
    
    impl ProductCategory for RawProductCategory {
        fn new(id: i32, name: String) -> Self {
            RawProductCategory {
                id,
                name
            }
        }
    }
    
    impl ProductCategory for UpperProductCategory {
        fn new(id: i32, name: String) -> Self {
            UpperProductCategory {
                id,
                name
            }
        }
    }
    
    impl ProductCategory for LowerProductCategory {
        fn new(id: i32, name: String) -> Self {
            LowerProductCategory {
                id,
                name
            }
        }
    }
    
    impl Queryable<product_category::SqlType, diesel::pg::Pg> for RawProductCategory {
        type Row = (i32, String);
        fn build(row: Self::Row) -> Self {
            ProductCategory::new(row.0, row.1)
        }
    }
    
    impl Queryable<product_category::SqlType, diesel::pg::Pg> for UpperProductCategory {
        type Row = (i32, String);
        fn build(row: Self::Row) -> Self {
            ProductCategory::new(row.0, row.1)
        }
    }
    
    impl Queryable<product_category::SqlType, diesel::pg::Pg> for LowerProductCategory {
        type Row = (i32, String);
        fn build(row: Self::Row) -> Self {
            ProductCategory::new(row.0, row.1)
        }
    }
    
    

    现在我注意到,对于 Queryable 的实现,我有相当多的代码重复,但我不想通过引入另一个包含实现 ProductCategory 特征的单个字段的结构来减少它。

    现在有趣的部分来了。我注意到为什么会出现这种情况,并打开了issue in diesel Github repository。如果此问题得到解决,我将相应地更新此答案,以展示实现相同目标的更好方法。

    【讨论】:

      猜你喜欢
      • 2016-12-23
      • 1970-01-01
      • 2021-07-02
      • 1970-01-01
      • 2012-03-15
      • 1970-01-01
      • 2018-03-25
      • 1970-01-01
      • 2019-12-23
      相关资源
      最近更新 更多