3

在我的模型中,有父母、孩子和命名的孩子组。组属于父母,孩子属于父母,孩子可能属于同一个父母的一组。

组可能会被删除,然后使用相同的名称但不同的 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 是 Child 类,而self不是 Child 对象。

我如何渴望加载这样的关联?还是我的模型结构很愚蠢?已经想到了组的复合主键,但我宁愿不这样做,因为默认情况下 rails 不支持。

4

2 回答 2

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
于 2012-05-19T17:03:17.967 回答
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
于 2012-05-19T16:38:08.130 回答