【问题标题】:How are joins in scopes in Rails 5.2 different from rails 5.1?Rails 5.2 中范围内的连接与 Rails 5.1 有何不同?
【发布时间】:2019-07-17 11:22:09
【问题描述】:

在我将 rails 从 5.1 升级到 5.2 后,我开始收到以下错误:

NoMethodError: undefined method `expr' for nil:NilClass
  from /gems_path/activerecord-5.2.0/lib/active_record/associations/join_dependency/join_association.rb:47:in `block in join_constraints'
  from /gems_path/activerecord-5.2.0/lib/active_record/associations/join_dependency/join_association.rb:33:in `reverse_each'
  from /gems_path/activerecord-5.2.0/lib/active_record/associations/join_dependency/join_association.rb:33:in `join_constraints'
  from /gems_path/activerecord-5.2.0/lib/active_record/associations/join_dependency.rb:167:in `make_constraints'
  from /gems_path/activerecord-5.2.0/lib/active_record/associations/join_dependency.rb:177:in `make_join_constraints'
  from /gems_path/activerecord-5.2.0/lib/active_record/associations/join_dependency.rb:104:in `block in join_constraints'
  from /gems_path/activerecord-5.2.0/lib/active_record/associations/join_dependency.rb:103:in `each'
  from /gems_path/activerecord-5.2.0/lib/active_record/associations/join_dependency.rb:103:in `flat_map'
  from /gems_path/activerecord-5.2.0/lib/active_record/associations/join_dependency.rb:103:in `join_constraints'
  from /gems_path/activerecord-5.2.0/lib/active_record/relation/query_methods.rb:1026:in `build_join_query'
  from /gems_path/activerecord-5.2.0/lib/active_record/relation/query_methods.rb:1008:in `build_joins'
  from /gems_path/activerecord-5.2.0/lib/active_record/relation/query_methods.rb:928:in `build_arel'
  from /gems_path/activerecord-5.2.0/lib/active_record/relation/query_methods.rb:903:in `arel'
  from /gems_path/activerecord-5.2.0/lib/active_record/relation.rb:554:in `block in exec_queries'
  from /gems_path/activerecord-5.2.0/lib/active_record/relation.rb:578:in `skip_query_cache_if_necessary'
  from /gems_path/activerecord-5.2.0/lib/active_record/relation.rb:542:in `exec_queries'
  from /gems_path/activerecord-5.2.0/lib/active_record/relation.rb:414:in `load'
  from /gems_path/activerecord-5.2.0/lib/active_record/relation.rb:200:in `records'
  from /gems_path/activerecord-5.2.0/lib/active_record/relation/delegation.rb:41:in `[]'

导致错误的代码如下所示:

class Post
  has_many :comments

  has_one :last_comment, -> {
    joins("LEFT JOIN posts on posts.id = comments.post_id")
    .where("
      comments.id = (
        SELECT MAX(comments.id) FROM comments
        WHERE comments.post_id = posts.id
      )"
    )
  }, class_name: "Comment"

  scope :with_last_comment, -> { joins(:last_comment) }
end

我创建了this gist,其中包含有助于重现错误的完整代码。将issue_with_joins_in_scopes_in_rails_5_3.rb 下载到您的PC 并使用

运行它
ruby issue_with_joins_in_scopes_in_rails_5_3.rb

您可以查看this Github issue了解更多详情

Rails 5.2 和 5.1 中的连接有什么区别导致代码 Post.with_last_comment 在 Rails 5.2 中失败并出现错误?

如何更改 Post 模型中的 last_comment 关联和 with_last_comment 范围,以便在 Rails 5.2 中工作?

【问题讨论】:

    标签: join activerecord ruby-on-rails-5.2


    【解决方案1】:

    发布关于如何为一组帖子预加载最后一条评论的部分解决方案

    首先,您需要有以下last_comment 关联:

    class Post < ApplicationRecord
      has_many :comments, dependent: :destroy
    
      has_one :last_comment, -> { order(id: :desc) }, class_name: "Comment"
    end
    

    它适用于 Rails 5.2 中的 preloadincludes 并生成以下 SQL 查询:

    Post.includes(:last_comment)
    
    Post Load (0.2ms)  SELECT  "posts".* FROM "posts" LIMIT ?  [["LIMIT", 11]]
    Comment Load (0.2ms)  SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = ? ORDER BY "comments"."id" DESC  [["post_id", 1]]
    

    这个解决方案的问题是,当我使用joins 时,它会忽略关联范围并生成以下 SQL 查询:

    Post.joins(:last_comment)
    
    Post Load (0.2ms)  SELECT  "posts".* FROM "posts" INNER JOIN "comments" ON "comments"."post_id" = "posts"."id" LIMIT ?  [["LIMIT", 11]]
    

    我能够通过以下方式更改原始 last_comment 关联来解决它:

    class Post < ApplicationRecord
      has_many :comments, dependent: :destroy
    
      has_one :last_comment, -> {
        joins(:post)                               # <--- Note this change
        .where("
          comments.id = (
            SELECT MAX(comments.id) FROM comments
            WHERE comments.post_id = posts.id
          )"
        )
      }, class_name: "Comment"
    
      scope :with_last_comment, -> { joins(:last_comment) }
    end
    

    现在Post.with_last_comment 生成以下 SQL:

    Post Load (0.3ms)  SELECT  "posts".* FROM "posts" INNER JOIN "comments" ON "comments"."post_id" = "posts"."id" INNER JOIN "posts" "posts_comments" ON "posts_comments"."id" = "comments"."post_id" AND (
          comments.id = (
            SELECT MAX(comments.id) FROM comments
            WHERE comments.post_id = posts.id
          )) LIMIT ?  [["LIMIT", 11]]
    

    Rails 5.2 中的连接与 Rails 5.1 有何不同的问题仍然悬而未决

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-20
      • 2017-06-09
      • 1970-01-01
      • 2011-06-30
      • 2014-08-26
      • 2014-10-09
      相关资源
      最近更新 更多