Rails [求助] rails 版本 4.1,select 后的对象被保存在 association

R_Rboys · 2014年05月28日 · 最后由 R_Rboys 回复于 2014年05月28日 · 1676 次阅读

Rails 版本 4.1.1 database: pg

我把 rails 的版本从 4.0 升到 4.1,在调整项目代码的时候,遇到这样的一个问题

class User < ActiveRecord::Base
    has_one :user_info

    delegate :total_points, to: :user_info
end
class UserInfo < ActiveRecord::Base
    belongs_to :user
end

当我执行下列代码的时候

user = UserInfo.select(:user_id).first.user #=> #<User id: 26, ...... >

神奇的事情就这样出现了

p user.user_info  #=> #<UserInfo id: nil, user_id: 26>

因为 user.user_info 返回的是 select 后的对象,user.total_points 会报错:【missing attribute: total_points】

补充:在 rails 4.0 里,user.user_info 不会返回 select 后的对象,而且重新去访问数据库,所以在 rails 4.0 是没问题

求助,求助

共收到 10 条回复

别沉,大神快来看下!

虽然不明白这个查询的意图,但是我这里是会重新查询一次的

irb(main):010:0> a = ArticleContent.select(:article_id).first.article
  ArticleContent Load (0.8ms)  SELECT  "article_contents"."article_id" FROM "article_contents"   ORDER BY "article_contents"."id" ASC LIMIT 1
  Article Load (0.9ms)  SELECT  "articles".* FROM "articles"  WHERE "articles"."id" = $1 LIMIT 1  [["id", 1]]
=> #<Article id: 1, user_id: 1, title: "12", slug: "12", created_at: "2014-05-26 08:26:05", updated_at: "2014-05-26 08:32:10", category_id: 1, published_at: "2014-05-26 08:26:00">

irb(main):011:0> a.content
  ArticleContent Load (1.0ms)  SELECT  "article_contents".* FROM "article_contents"  WHERE "article_contents"."article_id" = $1 LIMIT 1  [["article_id", 1]]
=> #<ArticleContent id: 2, article_id: 1, body: "<p>33</p><p><img src=\"/uploads/attachment/file/1/c...", created_at: "2014-05-26 08:43:53", updated_at: "2014-05-26 08:43:53">

irb(main):012:0> Rails.version
=> "4.1.1"
irb(main):013:0> 

@saiga 我这里是不会重新查询的,那么应该是我这里环境有问题!我再研究下

@saiga 你是一开始就是 4.1.1 的吧?

#4 楼 @hz_qiuyuanxin 4.0 -> 4.1rc -> 4.1.1

6楼 已删除

@saiga 我发现一个有趣的事情,你试着不要修改 article_content 在 article 的命名

class Article < ActiveRecord::Base
  has_one :article_content
end

再试着跑上面的例子,你就会出现跟我一样的错误!

#7 楼 @R_Rboys 还真是如此 = =....

#7 楼 @R_Rboys 在 ArticleContent 那加上 inverse_of : false 就好了。具体原因正在找

class ArticleContent < ActiveRecord::Base
  belongs_to :article, inverse_of: false
end

找到了,是 automatic_inverse_of 搞的鬼。

https://github.com/rails/rails/blob/923e274313dda8c772922e859df681f3d050a30e/activerecord/lib/active_record/reflection.rb#L466

这里判断了是否开启 automatic_inverse_of

def automatic_inverse_of
  if can_find_inverse_of_automatically?(self)
    inverse_name = ActiveSupport::Inflector.underscore(active_record.name).to_sym

    begin
      reflection = klass._reflect_on_association(inverse_name)
    rescue NameError
      # Give up: we couldn't compute the klass type so we won't be able
      # to find any associations either.
      reflection = false
    end

    if valid_inverse_reflection?(reflection)
      return inverse_name
    end
  end

  false
end

https://github.com/rails/rails/blob/923e274313dda8c772922e859df681f3d050a30e/activerecord/lib/active_record/associations/association.rb#L230

然后这里返回了 nverse_name

https://github.com/rails/rails/blob/923e274313dda8c772922e859df681f3d050a30e/activerecord/lib/active_record/associations/association.rb#L106

结果导致 rails 执行了 set_inverse_instance

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册