【问题标题】:Rails 3 - nested select and ".last"Rails 3 - 嵌套选择和“.last”
【发布时间】:2013-12-18 12:59:41
【问题描述】:

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

Part:
id
part_id
created_at
...

Parttest:
id
part_id
pass (boolean)
created_at

一个 Part 可以有多个 Parttest,一个 Parttest 属于一个 Part(在相关模型中设置为 has_many 和 belongs_to)。

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

我尝试过嵌套选择,但没有取得多大成功。我总是可以在控制器中编写逻辑代码来检查 Part.all.each 中的每个 Part 对象并将它们推送到一个数组中,但这感觉不是正确的方法。非常感谢所有帮助:)

【问题讨论】:

  • 如果您发现这些答案有帮助,您介意标记一个已接受的答案吗?干杯!

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


【解决方案1】:

编辑:误读你的帖子,你想做这样的事情reference

Part.joins(:parttests).where(parttests: {pass: true})会给你所有通过测试的零件

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

【讨论】:

  • 仅供参考...您可以使用order(created_at: :desc) 而不是传递显式字符串。您的范围内也存在语法错误。它缺少,--fixed。
  • @Mohamad - 我可能误解了您的解决方案,但它不起作用。我不能调用 Part.parttests 因为 parttests 不是 Part 类的方法。上面的代码不会给我parttest - 而不是通过测试的部分吗? (这是我想要的部分,而不是最完整的部分)
  • @SG84 看到我的回答!这是马克纳赫的答案,不是我的。我添加了自己的。
  • 此外,由于发布者对其进行了编辑,此代码还有另一个语法错误。 Parts 是复数形式。应该是Part。我会编辑。
【解决方案2】:

使用范围

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

【讨论】:

  • 我认为 OP 希望找到最近通过 Parttest 的 Parts - 而不是每个帖子最近通过的 Parttest
  • 没错——我只想考虑最近的parttest。
  • @SG84 我已经更新了我的答案。试试看。它应该可以工作。
  • @Mohamad - 感谢您的帮助。您上面的最后一次编辑看起来是正确的,我理解(我认为)它应该做什么,但是我在类中的两行都遇到了语法错误。 “语法错误,意外的'\ n',期待=>”两行,所以我猜可能对新的lambda语法不满意?我在使用 Rails 3.2.16 的 Ruby 2
  • @SG84 不客气。我认为在 Rails 3.x 上你不能使用这种语法。试试这个:has_many :passed_tests, ..., -> conditions: "pass = true" - 在这里阅读更多:guides.rubyonrails.org/v3.2.14/association_basics.html
【解决方案3】:

我最初发布了一个冗长的 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)) }

【讨论】:

    猜你喜欢
    • 2010-11-14
    • 1970-01-01
    • 2011-02-20
    • 1970-01-01
    • 2014-08-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多