【问题标题】:How to preload associations of STI models in Rails 7?如何在 Rails 7 中预加载 STI 模型的关联?
【发布时间】:2023-01-21 11:33:25
【问题描述】:

我的问题

想象一下我有那些模型:

class Absence << ApplicationRecord
  belongs_to :user
end

class Vacation << Absence
  belongs_to :vacation_contingent
end

class Illness << Absence; end

现在我想用

absences = Absence.where(user: xxx)

并遍历假期队伍

vacations = absences.select { |absence| absence.is_a?(Vacation)
vacations.each { |vacation| puts vacation.vacation_contingent.xxx }

现在我有 1 个针对这些缺席的数据库查询,每个 vacation_contingent 有 1 个 -> 坏

PS:我使用Absence.where而不是Vacation.where,因为我想用那些absences做其他事情。

我试过的

  1. 当然
    Absence.where(user: xxx).includes(:vacation_contingent)
    # -> ActiveRecord::AssociationNotFoundError Exception: Association named 'vacation_contingent' was not found`
    
    vacations = Vactions.where(user: xxx).includes(:vacation_contingent)
    other_absences = Absence.where(user: xxx).where.not(type: 'Vacation')
    

    但这一个很丑陋,我有 1 个数据库查询比我想要的多,因为我正在获取 2 次缺席。

    3.

    absences = Absence.where(user: xxx)
    vacations = absences.select { |absence| absence.is_a?(Vacation)
    preloader = ActiveRecord::Associations::Preloader.new
    preloader.preload(vacations, :vacation_contingent)
    # -> ArgumentError Exception: missing keywords: :records, :associations
    # -> (The initializer changed)
    
    absences = Absence.where(user: xxx)
    vacations = absences.select { |absence| absence.is_a?(Vacation)
    preloader = ActiveRecord::Associations::Preloader.new(records: vacations, associations: %i[vacation_contingent])
    # -> This line does nothing on it's own
    preloader.call
    # -> This just executes SELECT "vacation_contingents".* FROM "vacation_contingents" vacation.size times
    preloader.preload
    # -> Does the same as .call
    # -> And this doesn't even preload anything. When executing
    vacations.first.vacation_contingent
    # -> then the database is being asked again
    

【问题讨论】:

    标签: ruby-on-rails rails-activerecord ruby-on-rails-7


    【解决方案1】:

    在我看来,解决方案 2 是您可以使用 ActiveRecord 做的最好的事情。

    如果只需要一个请求,可以使用原始 SQL 来完成;就像是 :

    Absence.connection.select_all(%{SELECT * 
                                    FROM absences
                                    LEFT OUTER JOIN vacation_contingents ON absences.vacation_contingents_id = vacation_contingents.id
                                    WHERE absences.user_id = ?", user_xxx.id})
    

    它将返回 ActiveRecord::Result,每个 Absence 以及 AbsenceVacationContingent 的列都有一行

    【讨论】:

      猜你喜欢
      • 2018-11-02
      • 1970-01-01
      • 1970-01-01
      • 2011-08-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-25
      • 1970-01-01
      相关资源
      最近更新 更多