Rails 3 - 嵌套选择和“.last”

时间:2013-12-18 12:59:42

标签: ruby-on-rails ruby ruby-on-rails-3 activerecord chaining

我有一个场景似乎应该是在模型方面解决的事情,但无法找出有效的Activerecord方法的组合。我的数据库的相关部分如下:

Part:
id
part_id
created_at
...

Parttest:
id
part_id
pass (boolean)
created_at

零件可以有许多Parttests,Parttest属于单个零件(在相关模型中设置为has_many和belongs_to)。

在它的生命中 - 一个部件可以多次测试,可以通过,失败,通过等。 使用rails / activerecord我想检索最后(最近的)Parttest是一个传递的Parts的记录集。不应考虑早期的部分测试记录,仅供历史使用。

我尝试过嵌套选择而没有太大成功。我总是可以在控制器中对逻辑进行编码,以检查Part.all.each中的每个Part对象并将它们推送到数组中,但这并不是正确的方法。所有人都非常感谢:)

3 个答案:

答案 0 :(得分:0)

编辑:误读了您的帖子,您想要执行类似reference

的操作

Part.joins(:parttests).where(parttests: {pass: true})将为您提供所有通过考试的部分

尝试Part.joins(:parttests).last.where(parttests: {pass: true})

答案 1 :(得分:0)

使用范围

class Parttest
  scope :passed_tests, -> { where(pass: true) }
end

@part       = Part.find(1)                      # Fetch part instance
@part_tests = @part.parttests.passed_tests.last # Fetch its tests

或者在关联中使用条件:

class Part
  has_many :passed_tests, class_name: 'Parttest', -> { where(pass: true) }
end

@part_tests = @part.passed_tests.last

更新

现在我已正确理解了这个问题。怎么样?

class Part
  has_many :passed_tests, class_name: 'Parttest', -> { where(pass: true) }
  scope :with_last_passed_test, -> { joins(:passed_tests).last }
end
Part.with_last_passed_test

答案 2 :(得分:0)

我最初发布了一个冗长的SQL查询作为一个选项,但该建议不是很干净,所以我删除了它。这是一种将其分解为几个小块的方法。

假设我们从这些Parttest行开始:

id  part_id  created_at  pass
1   1        2014-04-26  true
2   1        2014-04-27  false
3   2        2014-04-26  false
4   2        2014-04-27  true

1)首先,我们想要找到每个Part的最新Parttest行。我们可以这样做:

# Parttest model
def self.most_recent
  max_rows = select("part_id, MAX(created_at) as max_created_at").group(:part_id)
  joins("INNER JOIN (#{max_rows.to_sql}) AS max_rows
      ON parttests.part_id = max_rows.part_id
     AND parttests.created_at = max_rows.max_created_at")
end

select会抓住这些值:

part_id  created_at
1        2014-04-27
2        2014-04-27

join会将我们与其他Parttest专栏联系起来:

id  part_id  created_at  pass
2   1        2014-04-27  false
4   2        2014-04-27  true

2)然后我们只想保留通过测试。我们可以这样做:

# Parttest model
scope :passed, -> { where(pass: true) }

将它与(1)结合起来,只有在通过调用传递测试时才能获得最新的测试:

Parttest.most_recent.passed

这只包括从我们的例子中返回这一行:

id  part_id  created_at  pass
4   2        2014-04-27  true

3)然后,为了找到最近测试通过的所有部件,我们可以提供此范围:

# Part model
scope :most_recent_test_passed, -> { where("id IN (?)", PartTest.most_recent.passed.map(&:part_id)) }