Ruby on Rails - 我应该为Album.first.photos覆盖哪种方法?

时间:2010-07-22 01:09:46

标签: ruby-on-rails ruby collections associative-array

我已经覆盖了find的方法ActiveRecord::Base,这很顺利,现在我可以很好地自定义它。

def self.find(*args)
  # my custom actions 
  super(*args)
end

然后

Album.find(1) #=> My custom result

但现在我想覆盖这个:

Album.first.photos

并且我不确定我应该覆盖哪种方法来完成工作...... 我正在考虑专门针对关联查询的内容,但我不确定:(

我需要对“ActiveRecord :: Base”采取行动,建议使用动态类,因此我无法为此创建方法photos,而是在我尝试的所有模型中进行交互的方法。

非常感谢


更新

为了更好地解释我想要实现的目标:

创建一个可插入的ruby gem用于rails并且gem可以简单地从数据库中获取ID结构并将其动态转换为缩短ID,就像bit.ly系统一样。但这是动态完成的,只向您的模型声明has_shortened :id,然后所有界面和查询返回例如DH3而不是12,例如,我已设法获得这就是我需要处理关联的问题。

关注宝石网址http://github.com/ludicco/shortener,以便您可以查看它,如果您认为这是一个不错的主意,请随时提出您可能需要实施的建议。

干杯

4 个答案:

答案 0 :(得分:2)

您在has_many定义的关联访问器在场景后面的ActiveRecord中非常复杂。它们实际上使用反射类来返回行为类似于模型类的代理对象,但将其查找器限制为外键(可能还有其他条件)。如果您想深入了解它,您可能需要查看ActiveRecord gem中的reflection.rb,associations.rb,associations / association_proxy.rb和associations / association_collection.rb。

但是,最后,我们可以使用关联访问器,就好像它本身就是类本身一样,ActiveRecord会将所需的附加条件传递给类查找器。

这意味着,如果您在场景后面调用Album.first.photos,则ActiveRecord会调用Photo.find(附加条件仅返回具有以下内容的记录:album_id => Album.first.id)。 / p>

因此,如果您覆盖Photo.find,如果您通过Album.first.photos使用它,它也会返回您修改后的结果。那是ActiveRecord魔术:)

我的快速测试(使用Rails 2.3.8运行):

class Album < ActiveRecord::Base
  has_many :photos
end

class Photo < ActiveRecord::Base
  belongs_to :album

  def self.find (*args)
    puts "XXX running custom finder"
    super
  end
end

Rails控制台:

> Photo.all
XXX running custom finder
Photo Load (0.4ms)   SELECT * FROM "photos" 
=> [#<Photo id: 1, album_id: 1>, … ] 

> Album.first.photos
Album Load (0.4ms)   SELECT * FROM "albums" LIMIT 1
XXX running custom finder
Photo Load (0.3ms)   SELECT * FROM "photos" WHERE ("photos".album_id = 1) 
=> [#<Photo id: 1, album_id: 1>, … ] 

答案 1 :(得分:1)

由于Album.first会返回相册的实例,您应该覆盖photos类本身的Album方法:

class Album < ActiveRecord::Base

  # ...

  def photos(*args)
    if my_condition_is_met
      // my custom handler
    else
      super
    end
  end

  # ...

end

现在,如果您确定每个模型实例都需要这个,我宁愿将它隔离在模块上:

module Photo
  def photos(*args)
    if my_condition_is_met
      // my custom handler
    else
      super
    end
  end
end

重新打开ActiveRecord::Base课程:

class ActiveRecord::Base
  include Photo
end

通过这种方式,您可以获得解决方案的模块化,使其成为“插件”。

一个额外且不相关的注意事项:您不必致电super(*args),因为只调用super会传递您收到的所有参数。

编辑:次要格式

答案 2 :(得分:0)

是否有特殊原因使用关联扩展名无法执行此操作?

http://ryandaigle.com/articles/2006/12/3/extend-your-activerecord-association-methods

直接与代理对象混淆是要求麻烦。但是,如果您只想覆盖查找,则可能会这样做。

答案 3 :(得分:0)

看看friendly_id插件(http://github.com/norman/friendly_id http://norman.github.com/friendly_id/file.Guide.html),它可以满足您的需求:为模型生成自定义ID别名。您可以定义自定义方法来生成slug。

在模型

中保留 id 字段

对于自定义#find使用:finder_sql选项:

class Album < ActiveRecord::Base
  has_friendly_id :short_id

  has_many :photos, :finder_sql => 'select * from photos where album_id="#{short_id}"'
end

>> Album.last.photos
select * from photos where album_id="t54"