在Ruby on Rails中,如何为has_many关系创建范围?

时间:2016-01-10 01:37:41

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

以下是一个例子:

假设我有一个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

现在,假设你要创建一个默认范围,这样如果学生没有评分的ReportCards,你想看到所有这些,但如果他们至少有一个评分的ReportCard,你只想看到评分的那些。最后,假设您按“semester_number”命令它们。

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

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

但我希望它是学生的默认范围,所以我尝试了:

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

但这不起作用。如果整个数据库中只有一个分级的report_card,它将不会返回任何report_cards。查看运行的查询,首先运行如下:

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”中应用的范围。也许这是不可能的。

3 个答案:

答案 0 :(得分:2)

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

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

答案 1 :(得分:1)

试试这个:

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

答案 2 :(得分:0)

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

has_many :users, -> { only_deleted }

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