【问题标题】:Ruby on Rails where query with relationsRuby on Rails where 关系查询
【发布时间】:2016-04-30 01:08:17
【问题描述】:

我正在尝试使用 where 查询与关系。

在这种情况下如何使用 where 与关系进行查询?

这是模型

User
  has_many :projects
  has_many :reasons, through: :projects

Project
  belongs_to :user
  has_many :reasons

Reasons
  belongs_to :project

这是无效的代码

  # GET /reasons
  def index
    reasons     = current_user.reasons
    updated_at  = params[:updated_at]

    # Filter with updated_at for reloading from mobile app
    if updated_at.present?



      # This one doesn't work!!!!!!!!!!!!      
      reasons = reasons.includes(:projects).where("updated_at > ?", Time.at(updated_at.to_i))



    # Get all non deleted objects when logging in from mobile app
    else
      reasons = reasons.where(deleted: false)
    end

    render json: reasons
  end  

---更新---

感谢@AmitA,这是正确的。

reasons = reasons.joins(:project).where("projects.updated_at > ?", Time.at(updated_at.to_i))

【问题讨论】:

    标签: sql ruby-on-rails ruby activerecord


    【解决方案1】:

    如果要查询项目有限制的所有原因,需要使用joins而不是includes

    reasons = reasons.joins(:project).where("projects.updated_at > ?", Time.at(updated_at.to_i))
    

    请注意,当includesjoins 都收到一个符号时,它们会寻找具有该精确名称的关联。这就是为什么你实际上不能做includes(:projects),但必须做includes(:project)joins(:project)

    另请注意,where 指定的连接表的约束必须引用表名,而不是关联名。这就是为什么我使用projects.updated_at(复数形式)而不是其他任何东西。换言之,当调用where 方法时,您处于“SQL 域”中。

    includesjoins 之间存在差异。 includes 运行一个单独的查询来加载依赖项,然后将它们填充到获取的活动记录对象中。所以:

    reasons = Reason.where('id IN (1, 2, 3)').includes(:project)
    

    将执行以下操作:

    1. 运行查询SELECT * FROM reasons WHERE id IN (1,2,3),为每条记录构造ActiveRecord对象Reason
    2. 查看获取的每个原因并提取其 project_id。假设这些是 11、12、13。然后运行查询 SELECT * FROM projects WHERE id IN (11,12,13) 并为每条记录构造 ActiveRecord 对象 Project
    3. 预填充在步骤 1 中获取的每个 Reason ActiveRecord 对象的 project 关联。

    上面的最后一步意味着你可以安全地做:

    reasons.first.project
    

    并且不会启动查询来获取第一个原因的项目。这就是为什么includes被用来解决N+1个查询的原因。但是,请注意 SQL 中不会出现 JOIN 子句 - 它们是单独的 SQL。所以使用includes时不能添加SQL约束。

    这就是joins 的用武之地。它只是连接表,以便您可以在连接的表上添加where 约束。但是,它不会为您预先填充关联。事实上,Reason.joins(:project) 永远不会实例化Project ActiveRecord 对象。

    如果您想同时使用joinsincludes,您可以使用第三种方法eager_load。您可以阅读更多关于差异的信息here

    【讨论】:

    • 很好的解释。我只是想补充一下,可以使用包含进行连接,并且它并不总是执行额外的查询来解决 N + 1 个查询。如果您使用包含,然后在基于包含表的过滤器中添加嵌套条件,您将获得左外连接。 guides.rubyonrails.org/…
    • 非常感谢您的出色回答。我也尝试了嵌套关系,有些部分有效,但有些部分无效。你介意看看这个相关的问题吗? @AmitA stackoverflow.com/questions/36952179/…
    猜你喜欢
    • 2023-04-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多