Diesel 中多个外键引用同一个表

The*_*rop 5 rust rust-diesel

我正在尝试创建一个引用同一个表两次的结构。这样做的目的是创建一种类别层次结构。这是我正在尝试对下表执行的操作:

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
);

Run Code Online (Sandbox Code Playgroud)

我正在尝试创建匹配的结构,如下所示:

#[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,
}

Run Code Online (Sandbox Code Playgroud)

我收到一条错误消息:

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)

Run Code Online (Sandbox Code Playgroud)

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

The*_*rop 1

因此,我一直在研究和研究 Diesel,正如上面的答案中已经指出的,这个问题的出现是由于特征的BelongsTo<Parent>定义方式所致。

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

// 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)
    }
}

Run Code Online (Sandbox Code Playgroud)

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

现在有趣的部分来了。我已经注意到为什么会出现这种情况,并在柴油 Github 存储库中提出了一个问题。如果这个问题得到解决,我将相应地更新这个答案,以展示实现相同目标的更好方法。