如前所述,活动记录关联创建了一系列便利方法的度量标准。当然,您可以编写自己的方法来获取所有内容。但这不是 Rails 方式。
Rails Way 是两个座右铭的结晶。DRY(不要重复自己)和“约定优于配置”。本质上,通过以一种有意义的方式命名事物,框架提供的一些健壮的方法可以抽象出所有公共代码。您在问题中放置的代码是可以通过单个方法调用替换的完美示例。
这些便捷方法真正发挥作用的地方是更复杂的情况。涉及连接模型、条件、验证等的东西。
为了在您执行类似的操作时回答您的问题@user.articles.find(:all, :conditions => ["created_at > ? ", tuesday])
,Rails 准备了两个 SQL 查询,然后将它们合并为一个。您的版本只返回对象列表。命名范围做同样的事情,但通常不跨越模型边界。
您可以通过在控制台中调用这些东西时检查 development.log 中的 SQL 查询来验证它。
所以让我们暂时讨论一下命名范围,因为它们给出了一个很好的例子来说明 rails 如何处理 SQL,我认为它们是一种更简单的方式来演示幕后发生的事情,因为它们不需要任何模型关联来炫耀。
命名范围可用于执行模型的自定义搜索。它们可以链接在一起,甚至可以通过关联来调用。您可以轻松创建返回相同列表的自定义查找器,但随后您会遇到问题中提到的相同问题。
class Article < ActiveRecord::Base
belongs_to :user
has_many :comments
has_many :commentators, :through :comments, :class_name => "user"
named_scope :edited_scope, :conditions => {:edited => true}
named_scope :recent_scope, lambda do
{ :conditions => ["updated_at > ? ", DateTime.now - 7.days]}
def self.edited_method
self.find(:all, :conditions => {:edited => true})
end
def self.recent_method
self.find(:all, :conditions => ["updated_at > ?", DateTime.now - 7 days])
end
end
Article.edited_scope
=> # Array of articles that have been flagged as edited. 1 SQL query.
Article.edited_method
=> # Array of Articles that have been flagged as edited. 1 SQL query.
Array.edited_scope == Array.edited_method
=> true # return identical lists.
Article.recent_scope
=> # Array of articles that have been updated in the past 7 days.
1 SQL query.
Article.recent_method
=> # Array of Articles that have been updated in the past 7 days.
1 SQL query.
Array.recent_scope == Array.recent_method
=> true # return identical lists.
这是事情发生变化的地方:
Article.edited_scope.recent_scope
=> # Array of articles that have both been edited and updated
in the past 7 days. 1 SQL query.
Article.edited_method.recent_method
=> # no method error recent_scope on Array
# Can't even mix and match.
Article.edited_scope.recent_method
=> # no method error
Article.recent_method.edited_scope
=> # no method error
# works even across associations.
@user.articles.edited.comments
=> # Array of comments belonging to Articles that are flagged as
edited and belong to @user. 1 SQL query.
本质上,每个命名范围都会创建一个 SQL 片段。Rails 将巧妙地与链中的每个其他 SQL 片段合并,以生成一个查询,准确地返回您想要的内容。关联方法添加的方法的工作方式相同。这就是它们与 named_scopes 无缝集成的原因。
mix & match 不起作用的原因与问题中定义的 of_sector 方法不起作用的原因相同。edit_methods 返回一个数组,其中edited_scope(以及作为链的一部分调用的 find 和所有其他 AR 便利方法)将它们的 SQL 片段向前传递到链中的下一个事物。如果它是链中的最后一个,它将执行查询。同样,这也行不通。
@edited = Article.edited_scope
@edited.recent_scope
您尝试使用此代码。这是正确的方法:
class User < ActiveRecord::Base
has_many :articles do
def of_sector(sector_id)
find(:all, :conditions => {:sector_id => sector_id})
end
end
end
要实现此功能,您需要执行以下操作:
class Articles < ActiveRecord::Base
belongs_to :user
named_scope :of_sector, lambda do |*sectors|
{ :conditions => {:sector_id => sectors} }
end
end
class User < ActiveRecord::Base
has_many :articles
end
然后你可以做这样的事情:
@user.articles.of_sector(4)
=> # articles belonging to @user and sector of 4
@user.articles.of_sector(5,6)
=> # articles belonging to @user and either sector 4 or 5
@user.articles.of_sector([1,2,3,])
=> # articles belonging to @user and either sector 1,2, or 3