如何在活动记录中累积查询条件?
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 关系是 惰性:因此,除非您通过 inspect
、each
或任何其他方式向它们请求数据,否则它们不会执行需要实际数据的方法。 where
没有。
你可以在这里使用一些重构,因为 if
s 的船载量绝不是 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)}
快速示例,
@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 关系是 惰性:因此,除非您通过 inspect
、each
或任何其他方式向它们请求数据,否则它们不会执行需要实际数据的方法。 where
没有。
你可以在这里使用一些重构,因为 if
s 的船载量绝不是 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)}