【问题标题】:Rails: With models A has_many B belongs_to C return A where B belongs to C and B is most recently created for given A?Rails:使用模型 A has_many B belongs_to C 返回 A 其中 B 属于 C 并且 B 是最近为给定 A 创建的?
【发布时间】:2010-02-11 19:50:19
【问题描述】:

我有A、B、C三个模型,其中A has_many BB belongs to CB has a created_on timestamp。 C has_many A 到 B。

我想返回 all A,其中 A 有一个属于 C 的 B,而 B 是该 A 最近创建的 B。

我可以通过方法循环来做到这一点。这可以单独使用 named_scopes 完成吗?还是其他一些优雅/有效的方式?

根据对真实世界 (TM) 示例的请求,A、B 和 C 可以想象为例如 Pets (A)、PetsName (B) 和 Names (C)。 PetsNames 在特定时间赋予 Pets,任何给定的 Name 都可以赋予多个 Pets。任何宠物都可以有多个名称(一个 :through 关系)。对于给定的名称,我想要 all 的宠物,其中该名称是该宠物最近创建的 PetName。调用可能类似于@name.pets.most_recently_named_as

【问题讨论】:

  • 很难理解使用 A,B,C。尝试编辑使用真正的名词(即配方,成分等......)我认为你会是一个更好的回应。
  • 我已按要求提供了一个真实世界的示例。

标签: ruby-on-rails has-many belongs-to


【解决方案1】:

Rails 的方法是在宠物上使用命名范围。

类似这样的:

class Pets
  has_many :pet_names
  has_many :names, :through => :pet_names

  named_scope :with_current_name, lambda {|name| 
    { :joins => "JOIN (pet_names pna, names) " +
        "ON (pna.pet_id = pets.id AND pna.name_id = names.id) " +
        "LEFT JOIN pet_names pnb " +
        "ON (pnb.pet_id = pets.id AND pna.created_at < pnb.created_at)", 
      :conditions => ["pnb.id IS NULL AND names.id = ? ", name]
    }
  }
end

Pets.with_current_name(@name)
@name.pets.with_current_name(@name)

为了保持以名称为中心,您始终可以在 C/Name 上定义一个调用命名范围的方法。

你总是可以做到的

class Name < ActiveRecord::Base
  has_many :pet_names
  has_many :pets, :through => :pet_names

  def currently_named_pets
    Pets.with_current_name(self)
  end
end

还有

@name.currently_named_pets

这是一个相当复杂的连接。这表明您可能应该重新考虑存储此信息的方式。为什么将名称放在单独的表中如此重要?

你最好做这样的事情:

nameold_name 列添加到Pet 后:

class Pet < ActiveRecord::Base
  serialize :old_name, Array
  def after_initialization 
    @old_name ||= []
  end

  def name= new_name
    self.old_name << new_name
    self.name = new_name
  end

  named_scope :with_name, lambda {|name|
     { :conditions => {:name => name}}
  }
end

【讨论】:

  • 非常感谢 EmFi,我将尝试使用该命名范围。我知道这是一个复杂的连接,这就是我问的原因。这里大大简化的示例只是我的真实模型的代理,它很好地独立,我绝对不能使用序列化。使用链接到 A/Pet 实例的方法来执行此操作的奖励点仍然存在。
  • 你能澄清一下你的意思吗?当从 A/Pet 实例链接时,所涉及的操作没有意义。此外,您不想通过链式方法来做到这一点。语句之间的 ActiveRecord 开销非常低效。但是,命名范围仍然适用于返回 As/Pets 的关联。 @store.pets.with_current_name(name)
  • 你是对的!我的意思是 C/Names(如问题的原始大纲所示)。所以@name.pets 让我得到了所有名为@name 的宠物。我想将一些东西链接到它以将宠物数组​​限制为@name 是最新名称的那些(以保持方法以名称为中心)。更多地考虑这一点,我认为它无法完成,所以你的解决方案似乎是要走的路。再次感谢您的帮助,并为造成的混乱道歉。
  • 如果这是您真正想要的,那么没有什么能阻止您在 C/Name 模型中定义一个方法,该方法调用 Pets 命名范围并提供 self 作为参数。
  • 对。实施您的解决方案,一些小问题 - :join 在您的答案中应该是 :joins,并且结尾的“`
相关资源