使用多个条件创建ActiveRecord范围

时间:2016-10-18 22:22:21

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

我有一个包含许多产品的Rails应用程序,其中一些产品与Issue模型相关联。其中一些产品有一个issue_id(所以产品为has_many产品),有些则没有。没有问题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的产品不包含在退回的集合中。

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

这是上面生成的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)),然后迭代以删除与尚未发布的问题相关的产品。第二次行动。

我觉得有些事情我不太关心在范围内做更复杂的查询。我理想的结果是一个修改后的范围,可以返回可用的产品,包括有问题和没有问题,并且不提供参数。

这是可能的吗,或者我现在应该辞职,因为我正在添加这些无关联的产品?

1 个答案:

答案 0 :(得分:4)

问题是您使用的是joins(:issue)。该方法在INNER JOINproducts表之间执行issues,并丢弃所有没有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])
}