参考has_many(Rails)中的实例

时间:2012-08-14 09:57:41

标签: ruby-on-rails ruby-on-rails-3 activerecord has-many

我有一个游戏模型has_many:文本。问题是我必须根据他们所属的游戏来区分不同的文本(是的,丑陋的,但它是遗留数据)。我创建了一个Text.in_game_order_query(game)方法,它返回了相应的顺序。

我最喜欢的解决方案是在Text模型中放置一个默认范围,但这需要知道它们属于哪个游戏。我也不想为每个游戏的文本创建单独的类 - 有很多游戏,有更多的游戏,所有较新的游戏将使用相同的顺序。所以我有另一个想法:在has_many中订购文本,当我知道它们属于哪个游戏时:

has_many :texts, :order => Text.in_game_order_query(self)

然而,自我就是这里的阶级,所以这不起作用。

除了每次调用@game.texts.in_game_order(@game)之外,真的没有其他解决方案吗?

6 个答案:

答案 0 :(得分:5)

我最近遇到了一个非常类似的问题,我确信在Rails中它是不可能的,但我学到了很多有趣的东西。

您可以为范围声明一个参数,然后不将其传入,默认情况下它将传递给父对象!

所以,你可以这样做:

class Game < ActiveRecord
  has_many :texts, -> (game) { Text.in_game_order_query(game) }

信不信由你,你不必通过游戏。 Rails会为你神奇地做。你可以这样做:

game.texts

但有一点需要注意。如果您启用了预加载,这在目前的Rails中将不起作用。如果您这样做,您可能会收到此警告:

  

弃权警告:关联范围&#39;文本&#39;是实例相关的(范围块采用参数)。预加载在创建单个实例之前发生。这意味着没有实例传递给关联范围。这很可能导致行为破裂或不正确。这些关联的加入,预加载和急切加载已弃用,将来将被删除。

答案 1 :(得分:3)

继续使用PradeepKumar的想法后,我找到了以下解决方案

假设一个类Block有一个属性block_type和一个容器类(比如Page),你可以这样:

class Page
  ...

  has_many :blocks do
    def ordered_by_type
      # self is the array of blocks
      self.sort_by(&:block_type)
    end
  end

  ...
end

然后当你打电话

page.blocks.ordered_by_type

你得到你想要的东西 - 由Proc定义。 显然,Proc可能要复杂得多,并且在SQL调用中没有工作,但是在编译结果集之后。

更新:
经过一段时间后,我重新阅读了这篇文章和我的回答,我想知道你是否可以做一些简单的事情,就像你基本上在帖子中提出的另一种方法一样。

如果您向名为Game

ordered_texts添加了一种方法,该怎么办?
  def ordered_texts
    texts.in_game_order(self)
  end

这是否解决了这个问题?或者这个方法是否需要与其他Game关系方法链接?

答案 2 :(得分:1)

Association extension是否有可能?

似乎你可以做到这一点:

module Legacy
  def legacy_game_order
    order(proxy_association.owner.custom_texts_order)
  end
end

class Game << ActiveRecord::Base
  includes Legacy
  has_many :texts, :extend => Legacy

  def custom_texts_order
    # your custom query logic goes here
  end
end

这样,给定游戏实例,您应该能够访问实例的自定义查询而无需传递自己:

g = Game.find(123)
g.texts.legacy_game_order

答案 3 :(得分:0)

这是一种可以做到的方法,

   has_many :texts, :order => lambda { Text.in_game_order_query(self) }

这是我通常不推荐的另一种方式(但会起作用),

has_many :texts do
  def game_order(game)
    find(:all, :order => Text.in_game_order_query(game))
  end
end

你可以通过

来打电话给他们
game.texts.game_order(game)

答案 4 :(得分:0)

我不确定你的订单/查询在in_game_order_query类方法中是什么样的,但我相信你可以这样做

has_many :texts, :finder_sql => proc{Text.in_game_order_query(self)}

只是让你知道我之前从未使用过这个,但如果你告诉我这是否适合你,我将不胜感激。

有关更多文档,请查看http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-has_many:finder_sql

答案 5 :(得分:-1)

我认为如果你想要处理运行时信息,你应该完成这个:

has_many :texts, :order => proc{ {Text.in_game_order_query(self)} }