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

时间:2015-06-26 10:32:12

标签: ruby-on-rails activerecord rails-activerecord

快速举例,

@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并在最终执行它。

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

有这样的好解决方案吗?

3 个答案:

答案 0 :(得分:2)

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)

这将使您的模型和控制器的单元测试更容易,因为您可以独立测试它们。在很长一段时间内,代码也更易于维护。

答案 1 :(得分:0)

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

它没有经常访问数据库,它只进行一次查询。澄清一下,我在谈论这个:

@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的中间值(因为这是分配返回的内容),控制台将尝试inspect它供您检查,因此,执行每个查询。

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

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

你可以在这里使用一些重构,因为if s的船只不会干,但它会起作用。

答案 2 :(得分:0)

试一试 -

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