查找 has_many_through 中未关联的对象

Finding objects that are not associated in has_many_through

我有简单的 类 像这样:

class Book
  has_many :book_categorizations
  has_many :categories, through: :book_categorizations, source: :book_category
end

class BookCategorizations
  belongs_to :book
  belongs_to :book_category
end

class BookCategory
  has_many :book_categorizations
  has_many :books, through: :book_categorizations
end

我想找到没有类别的 Book。如何使用 where?

查询

您可以将带有 LEFT JOINscope 添加到您的模型中:

# in book.rb
scope :without_categories, lambda {
  joins('LEFT JOIN book_categorizations ON books.id = book_categorizations.book_id').
    where(book_categorizations: { book_category_id: nil })
}

可以这样使用:

Book.without_categories
#=> returns books without a category

工作原理:

假设你有一个 fruits 和一个 colors table:

fruits
id | name
 1 | Apple
 2 | Orange
 3 | Banana

colors
id | name
 1 | black
 2 | red
 3 | yellow

和一个 colors_fruits 加入 table:

colors_fruits
color_id | fruit_id
2        | 1           # red Apple
3        | 3           # yellow Banana

由于 Rails' joins 方法生成 INNER JOIN,所有连接只会 return 具有至少一种颜色的水果。橙色不会出现在列表中,因为它没有颜色(因此无法加入):

Fruit.joins(:colors)
#=> red Apple, yellow Banana (simplified)

但是当我们对没有颜色的水果感兴趣时,我们就需要LEFT JOINLEFT JOIN 包括左侧 table 的所有元素 - 即使右侧 table 没有匹配(不幸的是,这种连接没有 Rails 助手):

Fruits.joins('LEFT JOIN colors_fruits ON colors_fruits.fruits_id = fruits.id')

这会生成如下结果:

id | color  | fruit_id | color_id
 1 | Apple  | NULL     | NULL
 2 | Orange | 2        | 1
 3 | Banana | 3        | 3

现在我们只需要排除那些没有 color_id

Fruits.joins('LEFT JOIN colors_fruits ON colors_fruits.fruits_id = fruits.id').
       where(colors_fruits: { color_id: nil })

您可能想了解 SQL JOINS. And there is this well known diagram about joins 的不同类型。