【问题标题】:In Ruby on Rails, how can I create a scope for a has_many relationship?在 Ruby on Rails 中,如何为 has_many 关系创建范围?
【发布时间】:2016-04-14 13:17:45
【问题描述】:

这是一个例子:

假设我有一个 Student 对象,它与 ReportCard 对象具有 has_many 关系。 ReportCard 对象有一个名为“graded”的布尔字段,用于标记它们已被评分。所以它看起来像:

class Student < ActiveRecord
  has_many :report_cards
end

class ReportCard < ActiveRecord
  # graded :boolean (comes from DB)
  belongs_to :student
end

现在,假设您要创建一个默认范围,以便如果学生没有评分的 ReportCard,您希望查看所有这些,但如果他们至少有一个评分 ReportCard,您只想查看评分的 ReportCard .最后,假设您按“学期编号”排序。

在 ReportCard 上使用此范围可以正常工作:

scope :only_graded_if_possible, ->(student) { where(graded: true, student: student).order(:semester_number).presence || order(:semester_number) }

但我希望它成为 Student 的默认范围,所以我尝试了:

class Student < ActiveRecord
  has_many :report_cards, ->{ where(graded: true).order(:semester_number).presence || order(:semester_number) }
end

但这不起作用。如果整个数据库中只有一个分级报告卡,它不会返回任何报告卡。查看正在运行的查询,首先它运行如下内容:

SELECT report_cards.* FROM report_cards WHERE reports_cards.graded = t ORDER BY semester_number ASC

我想这一定是现在?检查存在查询的一部分,并注意它根本没有过滤学生!因此,如果有一个已评分的 report_card,则检查通过,然后运行以下查询以获取返回的内容:

SELECT report_cards.* FROM reports_card WHERE report_card.student_id = 'student id here' AND report_card.graded = t ORDER BY semester_number

如果学生有评分报告卡,这个查询实际上是正确的,但如果他没有,则它总是空的。

我假设之后可能会添加对 Student 的过滤。所以我试图以某种方式让它立即过滤学生:

has_many :report_cards, ->{ where(graded: true, student: self).order(:semester_number).presence || order(:semester_number) }

这也不起作用,因为这个范围内的“self”似乎不是我假设的 Student 对象,而是所有 report_card id 的列表。这是这个运行的查询:

SELECT report_cards.* FROM report_cards WHERE report_cards.graded = t AND report_cards.student_id IN (SELECT report_cards.id FROM report_cards) ORDER BY semester_number ASC

这甚至不接近正确。我怎样才能让它工作?

我认为真正归结为有人能够将“self”(即当前的 Student 对象)作为参数传递到“has_many”中应用的范围内。也许那是不可能的。

【问题讨论】:

  • 你使用的是什么版本的 Rails?

标签: ruby-on-rails ruby activerecord scope has-many


【解决方案1】:

您可以将对象作为参数传递给 has_many 范围

has_many :report_cards, -> (student) { ... }

【讨论】:

  • 每次使用关联时都需要从:student.report_cards 更改为冗余的 student.reports_cards(student),这是我试图避免的。
  • 请解释一下。这不能回答我的问题。每个对 student.report_cards 的引用都会引发异常,因为参数的数量不再匹配。
  • 不,不会。 ActiveRecord 关系构建器将为您传递对象。试试看吧。
  • 哇,到目前为止似乎有效。我真的很困惑它如何神奇地获取参数。我永远不会想到这会奏效。我尝试将参数放在 report_cards 对象中的范围内,并在没有参数的情况下在 has_many 中调用该范围,但这不起作用。
  • 我想知道这是否真的完全支持。运行我的 rspec 测试时,其中一些测试突然开始失败,我收到非常有趣的警告:“弃用警告:关联范围‘report_cards’依赖于实例(范围块接受一个参数)。预加载发生在创建单个实例之前。这意味着没有实例被传递到关联范围。这很可能会导致损坏或不正确的行为。这些关联的加入、预加载和急切加载已被弃用,将来将被删除。"
【解决方案2】:

试试这个:

class Student < ActiveRecord::Base
  has_many :report_cards, ->{ where(graded: true).order(:semester_number).presence || unscoped.order(:semester_number) }
end

【讨论】:

    【解决方案3】:

    我在我的项目中使用它,我有一个与用户关联的模型:

    has_many :users, -> { only_deleted }
    

    并在用户模型中创建一个 only_deleted 范围,返回已删除的用户。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-06-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多