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

Posted

技术标签:

【中文标题】在 Diesel 中引用同一个表的多个外键【英文标题】:Multiple foreign keys referencing same table in 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 中尚未解决的一些固有限制吗?

【问题讨论】:

【参考方案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。如果此问题得到解决,我将相应地更新此答案,以展示实现相同目标的更好方法。

【讨论】:

以上是关于在 Diesel 中引用同一个表的多个外键的主要内容,如果未能解决你的问题,请参考以下文章

今日刷题总结10

外键引用oracle中两个表的一个字段

如何访问在 Diesel 中一般访问的表的具体列?

如何通过 UCanAccess 创建一个包含两个外键引用到另一个表的表?

使用 Django,如何添加引用同一个表的外键列? [复制]

Laravel Eloquent 关系 - 表的多列引用相同的外键