【问题标题】:Creating ActiveRecord scope with multiple conditionals使用多个条件创建 ActiveRecord 范围
【发布时间】:2016-10-18 22:22:21
【问题描述】:

我有一个包含许多产品的 Rails 应用程序,其中一些产品与问题模型相关联。其中一些产品有 issue_id(因此是 Issue has_many products),而有些则没有。 没有问题 ID 的产品是我正在开发的新产品。

我之前有一个命名范围,因此我可以使用Product.published 列出产品,如下所示:

scope :published, -> {
    joins(:issue).reorder('products.created_at ASC, products.number ASC')
    .where('products.status = ?', Product.statuses[:available])
    .where('issues.status = ?', Issue.statuses[:published])
}

这样做的结果是我只能找到与已发布问题(想想杂志问题)相关的产品。

我现在添加的产品与特定问题无关,但仍处于草稿/可用状态。上述范围没有找到这些产品,因为它会查找不存在的 issue_id。

我想我可以像这样修改范围,在最后一行添加 OR issue_id IS NULL 部分:

scope :published, -> {
    joins(:issue).reorder('products.created_at ASC, products.number ASC')
    .where('products.status = ?', Product.statuses[:available])
    .where('issues.status = ? OR issue_id IS NULL', Issue.statuses[:published])
}

但这不起作用。我仍然只获得与“已发布”问题相关的“可用”产品。没有 issue_id 的产品不包含在返回的集合中。

(在发布相关问题之前,有一个窗口将产品设置为可用,因此对于这些情况,我确实需要检查两个记录的状态。)

这是上面生成的 SQL(为便于阅读而包装):

pry(main)> Product.published.to_sql
=> "SELECT `products`.* FROM `products` INNER JOIN `issues` ON `issues`.`id` = 
`products`.`issue_id` WHERE (products.status = 1) AND (issues.status = 1 OR 
issue_id IS NULL)  ORDER BY products.created_at ASC, products.number ASC"

我已经创建了一个 Product 类方法,该方法将参数作为替代方法,但并非在所有情况下都有效,因为我经常根据 ID 查找产品,而事先不知道是否存在问题关联与否(例如,对于产品的 show 视图)。

另外,Product.published 简洁明了,替代方法是加载所有已发布的产品(例如,Product.where(:status => :published)),然后在第二次操作中迭代以删除与尚未发布的问题相关联的产品。

我觉得在一个范围内执行更复杂的查询时我不太了解。我的理想结果是修改后的范围,它可以返回可用的产品,无论有没有问题,并且不提供参数。

这可能吗,或者我现在应该放弃寻找替代方法,因为我正在添加这些不相关的产品?

【问题讨论】:

  • 使用例如Product.published.to_sql 查看此查询生成的 sql(编辑您的问题并在其中添加结果,以便我们都能看到):) 您通常可以弄清楚为什么 SQL 是错了,然后将您需要的修复程序反向移植到 ruby​​ 范围。
  • 好主意@TarynEast,谢谢。我已经添加了我之前检查过的生成的 SQL,但我并没有真正把我的头放在连接上,所以它没有帮助我发现问题。

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


【解决方案1】:

问题是您使用的是joins(:issue)。该方法在productsissues 表之间执行INNER JOIN,并丢弃所有没有issue_id 的产品。也许您可以使用LEFT JOIN,这样您就可以保留所有产品,无论它们有问题。

scope :published, -> {
    joins('LEFT JOIN issues ON issues.id = products.issue_id')
    .select('products.*')
    .reorder('products.created_at ASC, products.number ASC')
    .where('products.status = ?', Product.statuses[:available])
    .where('issues.status = ? OR products.issue_id IS NULL', Issue.statuses[:published])
}

【讨论】:

  • 成功了,谢谢!我显然需要学习更多实际的 SQL。
猜你喜欢
  • 1970-01-01
  • 2014-10-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多