Rails 6.1 の更新
ここで説明されているように、新しい Rails バージョンではこれが簡単になります。
.where.missing(:children)
古いバージョンについては、以下を参照してください。
レール 3 & 4
scope :without_children, includes(:children).where(:children => { :id => nil })
ここでの大きな違いは、joins
になることincludes
です。インクルードはすべてのリレーションをロードします。それらが存在する場合、結合は関連付けられたオブジェクトのみをロードし、リレーションのないオブジェクトを無視します。
実際にscope :with_children, joins(:children)
は、少なくとも 1 つの子を持つ Parent を返すのに十分なはずです。やってみて!
レール5
以下の@Ansonの回答を参照してください
gemはactiverecord_where_assoc
Rails 4.1 から 6.0 までこれを行うことができます。
scope :without_children, where_assoc_not_exists(:children)
自己参照関係はシームレスに処理されます。
joins
これにより、クエリが単一のレコードに対して複数の行を返すなどの問題も回避されます。
@MauroDias が指摘したように、それが親と子の間の自己参照関係である場合、上記のコードは機能しません。
ちょっとした調査で、私はそれを行う方法を見つけました:
次のモデルを検討してください。
class Item < ActiveRecord::Base
has_many :children, :class_name => 'Item', :foreign_key => 'parent_id'
子供のいないすべてのアイテムを返品する方法:
Item.includes(:children).where(children_items: { id: nil })
どうやってそのchildren_items
テーブルを見つけたのですか?
Item.joins(:children)
次の SQL を生成します。
SELECT "items".*
FROM "items"
INNER JOIN "items" "children_items"
ON "children_items"."parent_id" = "items"."id"
したがって、自己参照の場合に JOIN が必要な場合、Rails はテーブルを使用すると推測しました。
同様の質問: