【问题标题】:Rails has_many with `through` option "loses" joins?Rails has_many 与 `through` 选项“丢失”连接?
【发布时间】:2015-02-01 06:06:54
【问题描述】:

我有以下示例模型结构:

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

现在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...

这是因为这个关联执行的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'"

虽然visible_posts 通过在categories 上添加INNER JOIN 可以正常工作,但

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'"

为什么visible_posts_comments 似乎“丢失”了joins(:category) 语句但保留了merge(Category.active)?我认为没有理由故意放弃 through-association 的联接。这是错误还是功能?

我正在使用 activerecord-4.1.8。

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

【问题讨论】:

  • 如果查询使用includes而不是joins,是否也会发生这种情况?
  • @fjuan 将连接更改为包含,并且 visible_posts 也引发了相同的错误,缺少 FROM 子句

标签: ruby-on-rails rails-activerecord has-many-through


【解决方案1】:

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

1。 has_many :through 会从 through 关系中删除“joins”,源码:

#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

我认为他们这样做的原因是出于对记录创建操作的考虑。前任。也许 u.visible_posts_cmets.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

【讨论】:

  • 我接受答案,因为它在 ActiveRecord 的代码中,但我仍然不明白他们为什么这样做。调用has_many_through_association.create(...) 对我来说根本没有多大意义,因为永远不清楚创建的记录应该分配给(可能有很多)“通过”记录中的哪一个。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-03-04
  • 2014-09-01
  • 2012-02-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-06
相关资源
最近更新 更多