Rails has_many与`through`选项"失去"连接?

Ste*_*eer 11 ruby-on-rails has-many-through rails-activerecord

我有以下示例模型结构:

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
Run Code Online (Sandbox Code Playgroud)

现在User.first.visible_posts_comments引发以下错误:

ActiveRecord::StatementInvalid: PG::UndefinedTable: ERROR:  missing FROM-clause entry for table "categories"
LINE 1: ..." = "posts"."id" WHERE "posts"."user_id" = $1 AND "categorie...
Run Code Online (Sandbox Code Playgroud)

这是因为此关联执行的SQL如下:

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'"
Run Code Online (Sandbox Code Playgroud)

虽然visible_posts作品通过添加适当INNER JOINcategories,

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'"
Run Code Online (Sandbox Code Playgroud)

为什么visible_posts_comments似乎"失去" joins(:category)声明,但保持merge(Category.active)?我认为没有理由through故意放弃联合联盟.这是一个错误还是一个功能?

我正在使用activerecord-4.1.8.

可能与此有关:https://github.com/rails/rails/issues/17904

小智 5

我创建了一个和你一样的 rails 项目,发现了同样的问题。关于这个问题有两点:

1. has_many :through 将从直通关系中删除“连接”,源代码:

#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
Run Code Online (Sandbox Code Playgroud)

我认为他们这样做的原因是对记录创建操作的考虑。前任。也许 u.visible_posts_comments.create(...) 会让 ActiveRecord 感到困惑

2. 为您准备的绕行方式:

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
Run Code Online (Sandbox Code Playgroud)