在ActiveRecord中以DRY方式覆盖"查找"

时间:2020-03-05 18:58:03  来源:igfitidea点击:

我有一些模型需要在其上放置自定义查找条件。例如,如果我有一个Contact模型,则每次调用Contact.find时,我想限制返回的仅属于所使用帐户的联系人。

我是通过Google找到的(我对此做了一些自定义):

def self.find(*args)
  with_scope(:find => { :conditions =>  "account_id = #{$account.id}" }) do
    super(*args)
  end
end

除少数情况下account_id含糊不清之外,这非常有用,因此我将其调整为:

def self.find(*args)
  with_scope(:find => { :conditions =>  "#{self.to_s.downcase.pluralize}.account_id = #{$account.id}" }) do
    super(*args)
  end
end

这也很好用,但是我希望它是干的。现在,我有几种不同的模型希望使用这种功能。做这个的最好方式是什么?

当我们回答时,请包括代码以帮助我们的头脑掌握元编程Ruby-fu。

(我正在使用Rails v2.1)

解决方案

回答

我们没有告诉我们我们正在使用哪个版本的rails [将其编辑在rails 2.1上,因此以下建议是完全可行的],但是我建议我们使用以下形式,而不是使自己过载:

account.contacts.find(...)

这将自动将查找结果包装在包含user子句的范围内(因为我们拥有account_id,所以我假设帐户在某个地方很近)

我建议我们检查以下有关示波器的资源

  • http://ryandaigle.com/articles/2008/3/24/what-s-new-in-edge-rails-has-finder-functionality(这不再是边缘了:))
  • http://ryandaigle.com/articles/2008/8/20/named-scope-it-s-not-just-for-conditions-ya-know

回答

为了给问题一个具体的答案,我建议将上述方法移到要包含在所讨论的模型中的模块中;所以你有

class Contact
  include NarrowFind
  ...
end

PS。当心account_id的SQL转义,我们可能应该使用:conditions => [" .... =?",$ account_id]语法。

回答

尚的建议是正确的。假设模型如下所示:

class Contact < ActiveRecord::Base
  belongs_to :account
end

class Account < ActiveRecord::Base
  has_many :contacts
end

我们应该使用当前帐户的"联系人"关联,以确保仅获得该帐户的"联系人"记录,如下所示:

@account.contacts

如果我们想为联系人查询添加更多条件,则可以使用find进行指定:

@account.contacts.find(:conditions => { :activated => true })

而且,如果我们发现自己不断查询激活的用户,则可以将其重构为命名范围:

class Contact < ActiveRecord::Base
  belongs_to :account
  named_scope :activated, :conditions => { :activated => true }
end

然后我们将使用以下代码:

@account.contacts.activated