有一种暂时阻止ActiveRecord执行查询的好方法吗?

时间:2014-10-13 18:43:31

标签: ruby-on-rails ruby activerecord

我有一组ActiveRecord模型,我预先加载它们以避免N + 1问题。为了帮助完成这个过程,我希望能够进行一些手动测试,其中(概念上)块看起来如下所示:

# Prepare some data
data = Foo.includes(:bar, {baz: :quux}).find(27)

ActiveRecord.raise_error_if_any_queries_occur
perform_some_work_with_data(data)
ActiveRecord.back_to_normal

这似乎应该是一种方法,但我没有运气。还有其他人这样做过吗?

4 个答案:

答案 0 :(得分:5)

您可以断开ActiveRecord与数据库的连接,并在完成后重新连接:

data = Foo.includes(:bar, {baz: :quux}).find(27)

Foo.connection.disconnect!
perform_some_work_with_data(data)
Foo.connection.reconnect!

如果在ActiveRecord断开连接时尝试对数据库运行查询,则会引发错误。

答案 1 :(得分:0)

您可以通过以下方式断开与数据库的连接:

ActiveRecord::Base.connection.disconnect!
Rails.logger.info('Disconnected from database')

perform_some_work_with_data(data)

ActiveRecord::Base.establish_connection
Rails.logger.info('Connected to database')

但我认为这有点奇怪。我更喜欢使用bullet gem进行N + 1查询检测。

答案 2 :(得分:0)

rails中的查询将在某处调用此处定义的select_all(arel, name = nil, binds = [])方法: https://github.com/rails/rails/blob/89a7187cc0d893da67f53d3215a33043905d68ed/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb#L22

您可以看到此方法将处理select(sql, name = nil, binds = [])方法的查询。此外,由于使用了undef_method,因此该方法处理对其父方法的查询:选择您可以在其定义中选择的内容: https://github.com/rails/rails/blob/89a7187cc0d893da67f53d3215a33043905d68ed/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb#L354

最后,这意味着执行的select方法是ConnectionAdapter方法,例如posgresql适配器,如第946行所示: https://github.com/rails/rails/blob/89a7187cc0d893da67f53d3215a33043905d68ed/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb

所以,第一个选项: - 例如,您可以编辑其中一个文件以包含它们被调用的一些信息。

其次,我不知道它是否会起作用: - 您可以重写select_all(arel, name = nil, binds = [])以引发异常,例如:

  def select_all(arel, name = nil, binds = [])
    raise "error"
  end

如果您想取消此覆盖,您可以使用以下方法取消您的方法:

undef_method :select_all

所以你要把select_all传递给父。

但同样,我不知道第二个是否有效,但第一个确实有效,尽管编辑rails core以包含日志功能非常具有侵略性。

答案 3 :(得分:-2)

如果您删除find(27)并使用where(id: 27),则可以将.to_sql追加到最后。

data = Foo.includes(:bar, {baz: :quux}).where(id: 27).to_sql

然后用:

执行它
Foo.find_by_sql(data)

你也可以用Arel完成同样的事情。 Arel将允许您构建更复杂的查询,您可以在需要时执行这些查询。