将对象添加到自引用rails关联时自动更新/插入join-table字段

时间:2014-05-03 03:12:59

标签: ruby-on-rails activerecord ruby-on-rails-3.2

关于SO的第一篇文章,哇哦! RoR做到了:)在SO上有很多,甚至在RoR指南中也非常接近,但是我没有完全解决我想要做的事情。

我想对一种成分进行建模,其中每种成分也可能有一个有序的子成分列表 - (汇总)msyql表:

CREATE TABLE `ingredients`
  id
  name

CREATE TABLE `composite_ingredients`
  parent_id
  composite_id
  sort_order int
  CONSTRAINT `fk_one` FOREIGN KEY (`composite_id`) REFERENCES `ingredients` (`id`),
  CONSTRAINT `fk_two` FOREIGN KEY (`parent_id`) REFERENCES `ingredients` (`id`)

这是我到目前为止的建模方式

class Ingredient < ActiveRecord::Base
    attr_accessible :name

    has_many :composites, :class_name=>"CompositeIngredient",
                          :foreign_key=>'parent_id'

    has_many :sorted_composites, :through=>:composites, 
                                 :source=>:composite, 
                                 :class_name=>'Ingredient', 
                                 :order=>'sort_order asc'

class CompositeIngredient < ActiveRecord::Base
    attr_accessible :sort_order

    belongs_to :parent, :class_name=>'Ingredient', 
                        :foreign_key=>'parent_id'

    belongs_to :composite, :class_name=>'Ingredient', 
                           :foreign_key=>'composite_id'

在rails控制台中进行测试时,我能够成功地解决这个问题:

a=Ingredient.includes(:sorted_composites).first
b=Ingredient.find_by_name 'corn'
a.sorted_composites<<b

这一切都运行正常 - 现在'a'将'b'与它正确关联(即:在连接表composite_ingredients中有一条记录。)但是,sort_order字段位于CompositeIngredient对象中,这些对象在a.composites(而我在a.sorted_composites中有实际的Ingredient对象。)当我向a.sorted_composites添加成分时,我想在插入连接表之前设置sort_order ...这有意义吗?我基本上想要做到这一点:

a.composites.create :parent_id=>a.id, composite_id=>b.id, sort_order=>a.sorted_composites.size

当我这样做时

a.sorted_composites << b

一旦我完成了这一点,我的has_many的:order选项将处理sortd_composites从数据库出来时的顺序 - 它将进入我需要连接的连接表。

谢谢大家!

修改 排序顺序的原因:复合成分出现顺序的隐含值 - 排在列表前面的那些是其父级的“丰富”组件。例如,如果父母成分X按此顺序复合Y和Z,则X在X中比Z更丰富。

修改 在玩了一些之后,这似乎是我能够提出的最有意义的解决方案:

class Ingredient < ActiveRecord::Base
    attr_accessible :name

    has_many :composites, :class_name=>"CompositeIngredient",
                          :foreign_key=>'parent_id',
                          **:before_add=>:before_composite_add**
    ...
    def before_composite_add(new_composite)
        new_composite.sort_order = self.sorted_composites.size
    end

虽然是一个完整的RoR菜鸟,但我欢迎任何反馈/批评/建议。

1 个答案:

答案 0 :(得分:0)

我已经发表评论澄清了,我觉得最好给你一些关于它如何运作的进一步想法:

  

您当前的设置依赖于ingredients,然后可以   使用composite_ingredients联接模型关联。看起来像   composite_ingredients是一种自引用has_many :through加入   模型,这可能有点僵硬(如果你想改变未来的结构怎么办)

为什么不用ancestry gem将它们全部放在同一个模型中?这将允许您创建一个名为parent的列,gem将填充该列。这将允许您在没有连接模型的情况下将任何成分与child成分相关联:

@ingredient.children #-> all decendents of node

这将允许您取消连接模型,使您能够保留每个成分/子成分的sort_order,而不是分成连接模型