Ruby on Rails - 我应该为 Album.first.photos 重写哪个方法?

发布于 2024-09-10 17:26:13 字数 990 浏览 4 评论 0 原文

我已经覆盖了 ActiveRecord::Basefind 方法,效果非常好,现在我可以很好地自定义它。

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

然后

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

但是现在我想重写这个:

Album.first.photos

并且我不确定我到底应该重写什么方法来完成工作...... 我正在考虑一些专门用于关联查询的方法,但我不确定:(

我需要对建议动态类的“ActiveRecord::Base”采取行动,这样我就无法创建方法photos那,但是一种可以在我尝试的所有模型中进行交互的方法,

非常感谢


更新,

以便更好地解释我想要实现的目标:

创建一个与 Rails 一起使用的可插入 ruby​​ gem,并且该 gem 可以简单地获取 ID 结构。从数据库中将其即时转换为缩短的 ID,就像 bit.ly 系统一样。仅向您的模型声明 has_shortened :id 即可飞行,然后所有接口和查询都会返回例如 DH3 而不是 12 例如,我已经设法让这个工作了,我需要处理关联。

遵循 gem url http://github .com/ludicco/shortener,这样您就可以查看它,如果您认为这是一个好主意,请随时提出您可能需要实现它的想法。

干杯

I've overrode the method find for ActiveRecord::Base and that went pretty well, now I can customize it nicely.

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

then

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

But now I would like to override this:

Album.first.photos

and I'm not sure what method exactly I should override to get the job done...
I'm thinking of something specifically for associative queries, but I'm not sure :(

I need to act on 'ActiveRecord::Base' suggesting dynamic classes so I couldn't create a method photos for that, but a method that will interact within all the models I try.

Thanks a lot


Update

For better explaining what I'm trying to achieve:

Is to create a pluggable ruby gem for use with rails and that gem can simply take you ID structure from the database and convert it on the fly to a shorten ID just like bit.ly system. but this is done on the fly with only declaring has_shortened :id to your model, and then all the interface and queries return for example DH3 instead of 12 for example, I've managed to get this working this the point I need to deal with associataions.

Follow the gem url http://github.com/ludicco/shortener so you can check it out, and feel free to come with ideas you might have to implement it if you think it's a nice idea.

Cheers

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

野味少女 2024-09-17 17:26:13

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

然而,最终,我们可以使用关联访问器,就像它是类本身一样,并且 ActiveRecord 会小心地将所需的附加条件传递给类查找器。

这意味着,如果您在幕后调用 Album.first.photos,ActiveRecord 会调用 Photo.find (附加条件是仅返回具有 :album_id => 的记录专辑.first.id)。

因此,如果您重写 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>, … ] 

Association accessors like you define with has_many are pretty complicated stuff in ActiveRecord behind the scene. They actually use a reflection class to return a proxy object that behaves like a model class, but restrict its finders to the foreign key (and maybe other conditions). If you like to get deeper into it, you might want to have a look at reflection.rb, associations.rb, associations/association_proxy.rb and associations/association_collection.rb in the ActiveRecord gem.

However, in the end, we can use an association accessor as if it would be the class itself and ActiveRecord takes care to pass the required additional conditions to the class finder.

That means, that if you call Album.first.photos, behind the scene, ActiveRecord calls Photo.find (with additional condition to only return records that have :album_id => Album.first.id).

So, if you override Photo.find, it'll return your modified results as well if you use it through Album.first.photos. That's ActiveRecord magic :)

My quick test (ran with 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 console:

> 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>, … ] 
酒儿 2024-09-17 17:26:13

由于 Album.first 返回 Album 的实例,因此您应该重写 Album 类本身的 photos 方法:

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 将传递所有您收到的参数。

编辑:小格式

Since Album.first returns an instance of Album, you should override the photos method on the Album class itself:

class Album < ActiveRecord::Base

  # ...

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

  # ...

end

Now if you are sure you need this for every single model instance, I'd rather isolate it on a module:

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

And reopen ActiveRecord::Base class:

class ActiveRecord::Base
  include Photo
end

This way you get modularization for your solution, making it act like a "plugin".

One additional and unrelated note: you don't have to call super(*args), since a call to only super will pass along all the parameters you received.

EDIT: Minor formatting

美人骨 2024-09-17 17:26:13

看看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"

Take a look at friendly_id plugin (http://github.com/norman/friendly_id http://norman.github.com/friendly_id/file.Guide.html), it does what you need: generate custom id alias for model. You can define custom method to generate slug.

Leave id field in model

For custom #find use :finder_sql option:

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"
左秋 2024-09-17 17:26:13

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

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

直接修改代理对象是自找麻烦。但如果你只是想覆盖查找结果,这可能可以做到。

Is there an particular reason you can't do this with an association extension?

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

Messing with the proxy objects directly is asking for trouble. But if you just want to override the find, this might do it.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文