0

在我的应用程序中,我有一个复杂的数据库结构。为了生成调用的 JSON 响应,我需要从许多连接表中获取数据。

我创建了一个 SQL 查询来获取我需要的所有数据(使用 rails' includes)。但是,既然我的视图中有结果,我不知道实际渲染它的正确方法是什么。

一个简化的示例,而不是我的实际代码:假设我有一个包含包含任务的子项目的项目,并且查询返回它们所有的一些嵌套结构,根据一些逻辑进行过滤。

class Project < ActiveRecord::Base
  ...
  def with_sub_projects_visible_by(user)
    includes(...complex join with sub projects and tasks...)
    .where(...complex condition...)
  end
end

在控制器中,我有这样的东西:

  def show
    @project_with_full_details = Project.find(params[:id]).with_sub_projects_visible_by(current_user)
  end

有没有办法编写简单的视图代码来呈现包含所有数据的页面@project_with_full_details而无需再次调用数据库?根据我@project_with_full_details.sub_projects的尝试,简单地调用并不能解决问题。它调用数据库并获取相关 sub_projects 的完整列表,而不是应该包含在查询结果中的过滤后的列表。

(我使用 RABL 作为我的视图,因为我正在渲染 JSON,但它不太重要,ERB 示例将完全没问题)

4

1 回答 1

1

ActiveRecord 为关联提供了一个预加载器,这使得 Rails 只从数据库中加载对象一次(它也发生在加载页面之前)。

对于您的示例,它可能看起来像这样:

@project = Project.find(params[:id])
ActiveRecord::Associations::Preloader.new(
  @project, [ :subprojects, subprojects: :tasks]
)

这至少会给您只运行一次 SQL 的预期效果。

如果您的模型看起来像这样:

class Project
  has_many :users
  has_many :sub_projects
  has_many :tasks, through: :sub_projects
  ...
end

那么也许你可以定义一个像subprojects_for(user). 然后你应该能够按照以下方式做一些事情:

# controller
@project = Project.find(params[:id])
@subprojects = @project.subprojects_for(current_user)

# haml
.project
  = @project.title
  - @project.subprojects_for(current_user).each do |subproject|
    = Something for subprojects...
    - subproject.tasks.each do |task|
      = You get the idea

不过,真正的魔法应该发生在预加载器中。我假设您已经查看了服务器日志以查看 SQL 正在多次运行?

于 2012-07-02T18:42:18.293 回答