Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
518 views
in Technique[技术] by (71.8m points)

Rails 4 scope to find parents with no children

I found one answer that had some usable having examples for finding parents with n children, but the same is not usable for finding parents with no children (presumably since the join excludes them).

scope :with_children, joins(:children).group("child_join_table.parent_id").having("count(child_join_table.parent_id) > 0")

Can anyone point me in the right direction?

Question&Answers:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Rails 3 & 4

scope :without_children, includes(:children).where(:children => { :id => nil })

The big difference here is the joins becoming a includes: an include loads all the relations, if they exists, the join will load only the associated objects and ignore the object without a relation.

In fact, scope :with_children, joins(:children) should be just enough to return the Parent with at least 1 child. Try it out!


Rails 5

See @Anson's answer below


Gem activerecord_where_assoc

The activerecord_where_assoc gem can do this for Rails 4.1 up to 6.0.

scope :without_children, where_assoc_not_exists(:children)

Self-referencing relation are handled seemlessly.

This also avoids issues such as joins making the query return multiple rows for a single record.


As @MauroDias pointed out, if it is a self-referential relationship between your parent and children, this code above won't work.

With a little bit of research, I found out how to do it:

Consider this model:

class Item < ActiveRecord::Base
  has_many :children, :class_name => 'Item', :foreign_key => 'parent_id'

How to return all items with no child(ren):

Item.includes(:children).where(children_items: { id: nil })

How did I find that children_items table?

Item.joins(:children) generates the following SQL:

SELECT "items".* 
FROM "items" 
 INNER JOIN "items" "children_items" 
 ON "children_items"."parent_id" = "items"."id"

So I guessed that Rails uses a table when in need of a JOIN in a self-referential case.


Similar questions:


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...