Rails:急切地将belongs_to加载:引用self的条件

时间:2012-05-19 16:07:22

标签: ruby-on-rails eager-loading belongs-to

在我的模型中,有父母,孩子和有名儿童群体。群体属于父母,子女属于父母,子女可能属于同一父母群体。

可以删除组,然后使用相同名称但不同的ID重新创建组。子项按名称引用其组,而不是id,因此如果重新创建组,它将属于同一组。多个父母可能存在同名的组,因此我们需要区分它们。

class Parent < ActiveRecord::Base
  has_many :groups
  has_many :children
end

class Group < ActiveRecord::Base
  belongs_to :parent

  has_many :children,
    :foreign_key => :group_name,
    :primary_key => :name,
    :conditions => proc { "children.parent_id = #{self.parent_id}" }
end

class Child < ActiveRecord::Base
  belongs_to :parent

  belongs_to :group,
    :foreign_key => :group_name,
    :primary_key => :name,
    :conditions => proc { "groups.parent_id = #{self.parent_id}" }
end

这非常有效,直到我试图急切地加载儿童群体。 Child.where(...).includes(:group)给出undefined method parent_id' for #<Class:0x00000002dcc290>。条件proc中的self是Child类,而不是Child对象。

如何加载这样的关联?或者我的模型结构是愚蠢的?这些组的复合主键已经考虑过了,但我宁愿不这样做,因为rails默认情况下不支持它。

2 个答案:

答案 0 :(得分:0)

看起来:finder_sql选项适用于has_many关系。不幸的是,它不是belongs_to关系的选项。

class Group < ActiveRecord::Base
  belongs_to :parent

  has_many :children,
    :foreign_key => :group_name,
    :primary_key => :name,
    :finder_sql => proc { "select distinct children.id from children where children.parent_id = #{self.parent_id}" }
end

答案 1 :(得分:0)

我最终手动编写了Rails在加载时的功能:

class Child
  def self.eager_load_groups(children)
    keys = children.map {|c|
      "(#{connection.quote(c.parent_id)}, #{connection.quote(c.group_name)})"
    }
    keys.uniq!
    return if keys.empty?

    groups = Group.where('(parent_id, name) IN (' + keys.join(',') + ')')
    groups_by_key = Hash[groups.map {|g| [[g.parent_id, g.name], g] }]

    for c in children
      c.group = groups_by_key[[c.parent_id, c.group_name]]
    end
  end
end