如何在活动记录中累积查询条件?

How to accumulate query condition in active record?

快速示例,

@user = User.all
@user = @user.where(live: true) if params[:live]
@user = @user.where(another: true) if params[:another]
@user = @user.where(another2: true) if params[:another2]
@user = @user.where(another3: true) if params[:another3]
.
.
.

这段代码经常访问数据库,如果有很多参数的话

所以我想把搜索条件保存到一个var中,然后像这样在final执行。

where_condition += '.where(live: true)' if params[:live]
where_condition += @user.where(another: true) if params[:another]
where_condition += @user.where(another2: true) if params[:another2]
where_condition += @user.where(another3: true) if params[:another3]
    .
    .
    .

@user = User.all.where_condition

有这样好的解决方案吗?

实际上,没有。您的代码比您想象的要好。

它不会大量访问数据库,它只进行一次查询。澄清一下,我说的是这个:

@user = User.all
@user = @user.where(live: true) if params[:live]
@user = @user.where(another: true) if params[:another]
@user = @user.where(another2: true) if params[:another2]
@user = @user.where(another3: true) if params[:another3]
.
.
.

当通过 Rails 控制台 运行 输入给定代码时,您可能会有这种感觉。问题是您的应用程序中的行为略有不同。当逐行评估给定的代码时,您将获得 @user 的中间值(因为这是一个赋值 returns)控制台将尝试 inspect 它供您检查和因此,将执行每个查询。

您的应用程序代码不会这样做,因为它不会 运行 在交互式会话中。

ActiveRecord 关系是 惰性:因此,除非您通过 inspecteach 或任何其他方式向它们请求数据,否则它们不会执行需要实际数据的方法。 where 没有。

你可以在这里使用一些重构,因为 ifs 的船载量绝不是 DRY,但它会起作用。

Rails 使用 ActiveRecord 关系的惰性求值,因此您的代码不会多次访问数据库,而只会在查询求值时访问一次。

您可以通过查看日志轻松地检查它。您会注意到查询只执行一次。

因此,您的代码没有问题。尽管如此,您仍然可以采用一些改进。第一个是使用 ! 方法链接条件。

而不是

@user = User.all
@user = @user.where(live: true) if params[:live]
@user = @user.where(another: true) if params[:another]
...

你可以使用

@user = User.all
@user.where!(live: true) if params[:live]
@user.where!(another: true) if params[:another]

第二个,你绝对应该避免直接在控制器中构建条件链,因为这会使你的代码很难被测试。为了测试查询是否成功执行,您将必须构建一个完整的控制器测试。

重构模型方法中的代码。

class User
  def self.search(params)
    scope = all
    scope.where!(live: true) if params[:live]
    # ...
    scope
  end
end

在你的控制器中

@users = User.search(params)

这将使模型和控制器的单元测试更加容易,因为您可以独立测试它们。从长远来看,代码也更易于维护。

试试这个-

@user = User.all
@user = @user.select {|u| (params[:live] ? u.live : true) && (params[:another] ? u.another : true) && (params[:another2] ? u.another2 : true)}