如何在Rails 3 Active Record中链接“where”查询

8
我需要根据参数数据添加条件。
@users = User.where('id', params[:id]) unless params[:id].nil?
@users = User.where('email', params[:email]) unless params[:email].nil?
@users = User.limit(10)

但出于某些原因,它不起作用。 谢谢。
6个回答

25

每个语句都替换了变量@users,而且由于ActiveRecord进行懒加载的评估,前两个语句从未被调用。

如果您想保留三个独立的查询并以这种方式构建,请执行以下操作:

@users = User.limit(10)
@users = @users.where('id', params[:id]) if params[:id]
@users = @users.where('email', params[:email]) if params[:email]

虽然它不够漂亮,但它可以工作。不过,我建议将其保持为单个方法调用,并在模型中定义。

# In the model

def self.by_id_and_email(id, email)
  users = limit(10)
  users = users.where('id', id)       if id.present?
  users = users.where('email', email) if email.present?

  users
end

# In the controller / out of the model

User.by_id_and_email(params[:id], params[:email])

这样你就可以再次使用该方法,对其进行改进,并编写更快的测试。


那个方法有效,你知道是否有一种方法可以在第一行不使用限制查询来完成它吗? - Tamik Soziev
4
你可以只使用 scopedusers = scoped,它会返回一个没有任何条件的 ActiveRecord::Relation 对象。 - Parker Selbert

7
你可以为模型添加范围,如果没有提供参数,它们将不起作用。例如,(范围调用只返回当前范围)。
# in the model

def self.by_id(id)
    return scoped unless id.present?
  where(:id => id)
end

def self.by_email(email)
  return scoped unless email.present?
  where(:email => email)
end


# in the controller

User.by_id(params[:id]).by_email(params[:email])

1
正是我在寻找的! - Kimmo Lehto
我不得不将“scoped”替换为“all”,因为我认为在这种情况下,“scoped”已经被弃用了。 - idoimaging

3
你可以通过以下简单的方法实现这一点:
wheres = [:id, :email].map{|key| params.has_key?(key) ? {key => params[key]} : {} }\
                      .inject({}){|hash, injected| hash.merge!(injected)}
@users = User.where(wheres).limit(10)

此外,您可以将上述内容抽象为一个作用域。

2
使用作用域是解决这个问题的完美方法。请查看此asciicast以获取更深入的教程:http://asciicasts.com/episodes/215-advanced-queries-in-rails-3
通过使用作用域,您可以将where子句保持为单独的元素,可以单独重复使用,但也可以像where子句一样链接它们。

0

实际上,如果您为示例创建作用域会更好,例如:这是一篇很好地解释了此概念的文章

在您的模型中

scope :by_id, -> id { where(id: id) if id.present? }
scope :by_email, -> email { where(email: email) if email.present? }

在你的方法中,你可以这样调用

def my_function()
    User.by_id(id).by_email(email)
end

0
你可以通过类似以下的方式来实现...
if params[:id].exists? && params[:email].exists?
  @users = User.where('id = ? AND email = ?', params[:id], params[:email]).limit(10)
elsif #if only one of id/email exists then...
  @users = User.where( #conditions for only one of id/email ).limit(10)
else
  #raise some errors

实际上我有超过两个参数(可能多达5个不同的参数用于过滤)。 - Tamik Soziev
你给我投了反对票是因为你没有恰当地解释你的问题? - Norto23

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接