10

假设我们有这些模型

class Message
  belongs_to :messageable, polymorphic: true
end

class Ticket
  has_many :messages, as: :messageable
  has_many :comments
end

class User
  has_many :messages, as: :messageable
  has_many :ratings
end

class Rating
  belongs_to :user
end

class Comment
  belongs_to :ticket
end

现在我想加载所有消息(关联ticketsor users),并根据类的类型进行急切加载,无论是commentsfortickets还是ratingsforusers

当然Message.includes(:messageable).order("created_at desc")只会包含直接关联的对象,但问题是如何包含从每个模型类型派生的不同关联类型(即在本例中,如何预先加载comments for ticketsratings for users)?

这只是一个简单的例子,但是更复杂的情况呢,我想为user, 另一个关联包含其他内容,如果该关联需要更多包含怎么办?

4

3 回答 3

2

我能想到的唯一方法是使用通用名称复制每个模型上的关联:

class Ticket
  has_many :messages, as: :messageable
  has_many :comments
  has_many :messageable_includes, class_name: "Comment"
end

class User
  has_many :messages, as: :messageable
  has_many :ratings
  has_many :messageable_includes, class_name: "Rating"
end

Message.includes(:messageable => :messageable_includes) ...

我不确定我是否会将此策略用作广泛的解决方案,但如果您的情况如此复杂,它可能对您有用。

于 2013-02-26T19:31:09.697 回答
0

我在自己的项目中使用了以下辅助方法:

def polymorphic_association_includes(association, includes_association_name, includes_by_type)
  includes_by_type.each_pair do |includes_association_type, includes|
    polymorphic_association_includes_for_type(association, includes_association_name, includes_association_type, includes)
  end
end

def polymorphic_association_includes_for_type(association, includes_association_name, includes_association_type, includes)
  id_attr = "#{includes_association_name}_id"
  type_attr = "#{includes_association_name}_type"

  items = association.select {|item| item[type_attr] == includes_association_type.to_s }
  item_ids = items.map {|item| item[id_attr] }
  items_with_includes = includes_association_type.where(id: item_ids).includes(includes).index_by(&:id)

  items.each do |parent|
    parent.send("#{includes_association_name}=", items_with_includes[parent[id_attr]])
  end
end

这些可以让你说:

messages = Message.all
polymorhpic_association_includes messages, :messageable, {
  Ticket => :comments,
  User => :ratings
}

不是一个特别流畅的界面,但它通常可以工作。

于 2013-11-30T12:16:01.043 回答
0

将包含放在每个模型的默认范围内:

class Ticket
  has_many :messages, as: :messageable
  has_many :comments
  default_scope -> { includes(:comments).order('id DESC') }
end

class User
  has_many :messages, as: :messageable
  has_many :ratings
  default_scope -> { includes(:ratings).order('id DESC') }
end

然后,每当您调用Message.all每个多态关联时,都会包含它自己的资源。

此外,如果您需要在没有范围的情况下调用类,只需使用unscoped或创建不同的范围:

class Ticket
  has_many :messages, as: :messageable
  has_many :comments
  has_many :watchers
  default_scope -> { includes(:comments).order('id DESC') }
  scope :watched -> {includes(:watchers)}
end

Ticket.unscoped.all # without comments or watchers (or order)
Ticket.watched.all  # includes watchers only
于 2014-07-30T10:07:48.120 回答