I have the following example model structure:
class Category < ActiveRecord::Base
has_many :posts
scope :active, -> { where(active: true) }
end
class User < ActiveRecord::Base
has_many :posts
has_many :visible_posts, -> { joins(:category).merge(Category.active) }, class: Post
has_many :visible_posts_comments, through: :visible_posts, source: :comments
has_many :comments
end
class Post < ActiveRecord::Base
belongs_to :category
belongs_to :user
has_many :comments
end
class Comment < ActiveRecord::Base
belongs_to :post
belongs_to :user
end
Now a User.first.visible_posts_comments
raises the following error:
ActiveRecord::StatementInvalid: PG::UndefinedTable: ERROR: missing FROM-clause entry for table "categories"
LINE 1: ..." = "posts"."id" WHERE "posts"."user_id" = $1 AND "categorie...
Which is because the SQL which is executed by this association is the following:
2.1.2 :009 > u.visible_posts_comments.to_sql
=> "SELECT \"comments\".* FROM \"comments\" INNER JOIN \"posts\" ON \"comments\".\"post_id\" = \"posts\".\"id\" WHERE \"posts\".\"user_id\" = $1 AND \"categories\".\"active\" = 't'"
While visible_posts
works properly by adding the INNER JOIN
on categories
,
2.1.2 :010 > u.visible_posts.to_sql
=> "SELECT \"posts\".* FROM \"posts\" INNER JOIN \"categories\" ON \"categories\".\"id\" = \"posts\".\"category_id\" WHERE \"posts\".\"user_id\" = $1 AND \"categories\".\"active\" = 't'"
why does visible_posts_comments
seem to "lose" the joins(:category)
statement but keeps the merge(Category.active)
? I see no reason to drop the joins of the through
-association on purpose. Is this a bug or a feature?
I am using activerecord-4.1.8.
Could be related to this: https://github.com/rails/rails/issues/17904
I've created a rails project the same as yours, found the same issue. Two points on this issue:
#lib/active_record/associations/through_association.rb line 14
def target_scope
scope = super
chain.drop(1).each do |reflection|
relation = reflection.klass.all
relation.merge!(reflection.scope) if reflection.scope
scope.merge!(
relation.except(:select, :create_with, :includes, :preload, :joins, :eager_load)
)
end
scope
end
I think the reason they did like this is a consideration of record creating operations. ex. Maybe u.visible_posts_comments.create(...) will make ActiveRecord confused
class Category < ActiveRecord::Base
has_many :posts
end
class User < ActiveRecord::Base
has_many :posts
has_many :visible_posts, -> { merge(Post.active) }, class: Post
has_many :visible_posts_comments, -> { joins(:post).merge(Post.active) }, class: Comment
has_many :comments
end
class Post < ActiveRecord::Base
belongs_to :category
belongs_to :user
has_many :comments
scope :active, -> { joins(:category).merge(Category.active) }
end
class Comment < ActiveRecord::Base
belongs_to :post
belongs_to :user
end
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With