如何在Rails中递增地应用named_scopes

时间:2009-12-29 15:32:30

标签: ruby-on-rails

named_scope :with_country, lambad { |country_id| ...}

named_scope :with_language, lambad { |language_id| ...}

named_scope :with_gender, lambad { |gender_id| ...}

if params[:country_id]
  Event.with_country(params[:country_id])
elsif params[:langauge_id]
  Event.with_state(params[:language_id])
else 
  ......
  #so many combinations
end

如果我同时获得国家和语言,那么我需要同时应用它们。在我的实际应用程序中,我有8个不同的named_scopes可以根据情况应用。如何逐步应用named_scopes或在某处保留named_scopes,然后一次性应用。

我试着坚持这样的价值

 tmp = Event.with_country(1)

但是会立即触发sql。

我想我可以写点像

if !params[:country_id].blank? && !params[:language_id].blank? && !params[:gender_id].blank?
  Event.with_country(params[:country_id]).with_language(..).with_gender
elsif country && language
elsif country &&  gender
elsif country && gender
 .. you see the problem

6 个答案:

答案 0 :(得分:2)

实际上,SQL不会立即触发。虽然我没有费心去查看Rails如何消除这种魔力(尽管现在我很好奇),但在实际检查结果集的内容之前,查询不会被触发。

因此,如果您在控制台中运行以下命令:

wc = Event.with_country(Country.first.id);nil # line returns nil, so wc remains uninspected
wc.with_state(State.first.id)

您会注意到第一行没有触发事件查询,而第二行触发了一个大型事件查询。因此,您可以安全地将Event.with_country(params[:country_id])存储为变量,并在以后添加更多范围,因为查询只会在最后触发。

要确认这是真的,请尝试我正在描述的方法,并检查您的服务器日志以确认在事件的页面本身上只触发了一个查询。

答案 1 :(得分:1)

检查Anonymous Scopes

答案 2 :(得分:1)

我必须做类似的事情,在视图中应用了许多过滤器。我所做的是创建具有条件的named_scopes:

named_scope :with_filter, lambda{|filter| { :conditions => {:field => filter}} unless filter.blank?}

在同一个类中,有一个方法从动作接收参数并返回过滤后的记录:

def self.filter(params)
  ClassObject
  .with_filter(params[:filter1])
  .with_filter2(params[:filter2])
end

您可以使用named_scopes添加所有过滤器,并根据发送的参数使用它们。

我从这里接受了这个想法:http://www.idolhands.com/ruby-on-rails/guides-tips-and-tutorials/add-filters-to-views-using-named-scopes-in-rails

答案 3 :(得分:0)

Event.with_country(params[:country_id]).with_state(params[:language_id])

将起作用,并且不会在结束之前触发SQL(如果你在控制台中尝试它,它会马上发生,因为控制台会在结果上调用to_s.IRL SQL将不会触发直到结束)。

我怀疑你还需要确保每个named_scope测试传入的内容的存在:

named_scope :with_country, lambda { |country_id| country_id.nil? ? {} : {:conditions=>...} }

答案 4 :(得分:0)

使用Rails 3很容易:

products = Product.where("price = 100").limit(5) # No query executed yet
products = products.order("created_at DESC")     # Adding to the query, still no execution
products.each { |product| puts product.price }   # That's when the SQL query is actually fired

class Product < ActiveRecord::Base
  named_scope :pricey, where("price > 100")
  named_scope :latest, order("created_at DESC").limit(10)
end

答案 5 :(得分:0)

简短的回答是根据需要简单地调整范围,根据存在的参数缩小范围:

scope = Example

# Only apply to parameters that are present and not empty
if (!params[:foo].blank?)
  scope = scope.with_foo(params[:foo])
end

if (!params[:bar].blank?)
  scope = scope.with_bar(params[:bar])
end

results = scope.all

更好的方法是使用像Searchlogic(http://github.com/binarylogic/searchlogic)这样的东西来封装所有这些。