ActiveRecord has_many关系忘了改变孩子?

时间:2016-10-08 18:19:15

标签: ruby-on-rails activerecord

我有一个类型A的实例,其中包含了很多Bs。当A.foo = value方法被调用时,我实际上想要编写一个委托给foo的方法=调用A的第一个B。

class A < ActiveRecord::Base
  has_many :bs, autosave: true

  def foo
    bs.first.foo
  end
  def foo=(val)
    bs.first.foo = val
  end
end

class B < ActiveRecord::Base
  belongs_to A
end


rails generate model A 
rails generate model B a:references foo:string


2.3.0 :001 > a = A.create!
   (0.1ms)  begin transaction
  SQL (0.3ms)  INSERT INTO "as" ("created_at", "updated_at") VALUES (?, ?)  [["created_at", "2016-10-08 18:03:18.255107"], ["updated_at", "2016-10-08 18:03:18.255107"]]
   (7.8ms)  commit transaction
 => #<A id: 1, created_at: "2016-10-08 18:03:18", updated_at: "2016-10-08 18:03:18"> 

创建一个A并将其命名为a

2.3.0 :002 > b = B.create!(a: a, foo: "initial")
   (0.4ms)  begin transaction
  SQL (0.4ms)  INSERT INTO "bs" ("a_id", "foo", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["a_id", 1], ["foo", "initial"], ["created_at", "2016-10-08 18:03:40.658035"], ["updated_at", "2016-10-08 18:03:40.658035"]]
   (8.3ms)  commit transaction
 => #<B id: 1, a_id: 1, foo: "initial", created_at: "2016-10-08 18:03:40", updated_at: "2016-10-08 18:03:40"> 

创建一个B并将其命名为b。让它成为A的孩子。将它的foo属性设置为&#34; initial&#34;。

2.3.0 :003 > a.reload.foo
  A Load (0.2ms)  SELECT  "as".* FROM "as" WHERE "as"."id" = ? LIMIT 1  [["id", 1]]
  B Load (0.2ms)  SELECT  "bs".* FROM "bs" WHERE "bs"."a_id" = ?  ORDER BY "bs"."id" ASC LIMIT 1  [["a_id", 1]]
 => "initial" 

检查a是否看到了新生儿foo:是的。正如所料。

2.3.0 :004 > a.foo = "set"
  B Load (0.1ms)  SELECT  "bs".* FROM "bs" WHERE "bs"."a_id" = ?  ORDER BY "bs"."id" ASC LIMIT 1  [["a_id", 1]]
 => "set" 
2.3.0 :005 > a.foo
  B Load (0.4ms)  SELECT  "bs".* FROM "bs" WHERE "bs"."a_id" = ?  ORDER BY "bs"."id" ASC LIMIT 1  [["a_id", 1]]
 => "initial" 

Whaaat?我刚刚打电话给a.foo = "set"。现在,当我再次致电a.foo读取值时,我得到"initial"?这不是has_one关系的工作方式。为什么ActiveRecord每次都会从数据库重新加载,而不是缓存它的查询?

最终,我打算致电a.save!,让它自动保存到b。但如果这种关系对每一个未决的变化都有失忆,那就不可能了。这是怎么回事?!

1 个答案:

答案 0 :(得分:1)

在A和B之间设置has_one关系,并将foo委托给has_one关联。

class A
  has_many :bs
  has_one :first_b, -> { first },
    class_name: 'B'
  delegate :foo, to: :first_b
end

要避免查询B,您可以使用.joins, includes or eager_load