【问题标题】:Ruby includes with scopeRuby 包含范围
【发布时间】:2020-08-14 13:32:21
【问题描述】:

在我的 Rails 6 应用程序中,我有定义 active 范围的模型 Journey:

class Journey < ApplicationRecord
  has_many :users, through: :journey_progresses
  has_many :journey_progresses, dependent: :destroy

  scope :active, -> { where(is_deleted: false) }
end

在我的端点中,我想显示用户 Journey_progress 和序列化器旅程本身。为此,我使用下面的查询效果很好

      def query
        current_user.journey_progresses.includes(:journey)
      end

如何修改上述查询以仅显示活动旅程?我试图添加像current_user.journey_progresses.includes(:journey).where(is_deleted: false) 这样的位置,但我收到一个错误:

Caused by PG::UndefinedColumn: ERROR:  column journey_progresses.is_deleted does not exist

根据this answer,我尝试使用current_user.journey_progresses.includes(:journey).where(journey: {is_deleted: false} ),但又遇到了另一个错误:

Caused by PG::UndefinedTable: ERROR:  missing FROM-clause entry for table "journey"

这种操作的正确语法是什么?

【问题讨论】:

标签: ruby-on-rails ruby


【解决方案1】:

您可以合并连接模型的范围。

current_user.journey_progresses.joins(:journey).merge(Journey.active)

这比测试is_deleted 布尔值要好,因为将来,如果激活旅程的定义发生变化,您将无需修改上述行。

【讨论】:

  • 好的,但这不会减慢查询速度吗? include 是一个很好的解决方案,我担心 N+1 查询......
  • 它根本不起作用 - 我得到 [] 而不是 Journeys_progress 列表
  • @mr_muscle 在担心任何 N+1 性能问题之前,您应该首先让您的查询正常工作
  • 我的查询在没有is_deleted 的情况下运行良好。随着你的合并,我什么也得不到 - []
  • 只是检查...您在创建列时是否设置了默认值 false?您可能在数据库中有 nil 而不是 false。定义范围时尝试scope :active, -&gt; { where(is_deleted: [nil, false]) }
【解决方案2】:

您可以尝试以下方法吗?

current_user
  .journey_progresses.
  .joins(:journey)
  .where("journeys.is_deleted = ?", false)

【讨论】:

    【解决方案3】:
    current_user.journey_progresses.includes(:journey).where(journey: {is_deleted: false} )
    

    Rails 5 提供上述语法。

    您可以尝试以下可能的方法,

    1. 添加自定义关联

    JourneyProgress 模型中

    class JourneyProgress < ApplicationRecord
        belongs_to :active_journey, -> { where(is_deleted: false) }, class_name: 'Journey'
    end
    

    并在查询时包含关联。

        current_user.journey_progresses.includes(:active_journey)
    

    1. 添加默认范围 - 但此范围将始终应用于从该模型进行查询(https://apidock.com/rails/ActiveRecord/Base/default_scope/class)

    default_scope -&gt; { where(is_deleted: false) }

    【讨论】:

    • 我使用的是 Rails 6,这个查询 current_user.journey_progresses.includes(:journey).where(journey: {is_deleted: false} ) 不起作用。失败并出现错误:ActiveRecord::StatementInvalid: PG::UndefinedTable: ERROR: missing FROM-clause entry for table "journey",如前所述。
    • 好的。那你可以试试我分享的方法。
    • 那么基于共享代码,你会在app中使用已删除记录和未删除记录吗?
    • 是的,我会同时使用它们。
    • @mr_muscle 查询不起作用的原因是在传递嵌套的 where 属性时,您必须指定表的名称,而不是关联的名称。 .where(journey: {...}) 应该是.where(journeys: {...}),表名默认是复数。
    【解决方案4】:

    你在这里误用了includesincludes 仅用于优化,以减少进入数据库的查询次数。

    由于要根据journey 中的值过滤行,因此需要加入它:

    current_user.journey_progresses.joins(:journey)
    

    作为对这类问题的一般建议,我建议查看 Rails 日志以查看最后生成的 SQL。处理多个has_manyscopeinclude 等会很快变得混乱。

    更新

    您实际上可以像这样将joinsincludes 结合起来:

    Foo.includes(:bars).where('bars.name = ?', 'example').references(:bars)
    

    https://api.rubyonrails.org/classes/ActiveRecord/QueryMethods.html#method-i-includes

    【讨论】:

    • 这取决于查询。有时,当您使用includes 时,活动记录会生成连接。例如,在 CanCan 应用程序中,命令 User.includes(:role).where(roles: {id:1}).to_sql 在控制台中显示 LEFT JOIN。由于字符限制,我无法输入输出查询。
    • 还有其他方法可以实现吗?包含用于轻松加载,并通过延迟加载连接工作。所以我认为这是不合适的。
    • 你的意思是eager loading
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-11-27
    • 1970-01-01
    • 1970-01-01
    • 2013-04-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多