升级到Rails 4.2.6后,has_one的奇怪行为

时间:2016-04-06 10:30:51

标签: ruby-on-rails ruby-on-rails-4.2 has-one

我有两种模式:

class User < ActiveRecord::Base
  belongs_to :group, dependent: :destroy

  validates :login, presence: true

  before_create :add_group

  private

  def add_group
    create_group(name: login)
  end
end

class Group < ActiveRecord::Base
  has_one :user
end

当我尝试创建用户时,我得到SystemStackError: stack level too deep。 所以我改变了这样的add_group回调:

def add_group
  create_group(name: login) unless group
end

现在,它保存了用户并正确设置了group_id,但是在没有验证的情况下创建了User的第二个空记录。 SQL:

>> u = User.create!(login: "foo")
   (0.7ms)  BEGIN
  SQL (2.2ms)  INSERT INTO "groups" ("name", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id"  [["name", "foo"], ["created_at", "2016-04-06 10:09:52.981954"], ["updated_at", "2016-04-06 10:09:52.981954"]]
  Group Load (1.2ms)  SELECT  "groups".* FROM "groups" WHERE "groups"."id" = $1 LIMIT 1  [["id", 1453]]
  SQL (2.0ms)  INSERT INTO "users" ("login", "created_at", "updated_at", "group_id") VALUES ($1, $2, $3, $4) RETURNING "id"  [["login", "foo"], ["created_at", "2016-04-06 10:09:52.923926"], ["updated_at", "2016-04-06 10:09:52.923926"], ["group_id", 1453]]
  SQL (1.4ms)  INSERT INTO "users" DEFAULT VALUES RETURNING "id"
   (9.1ms)  COMMIT

最后一个INSERT INTO用户表是奇数。 我无法弄清楚为什么会发生这种情况......

我找到了两个解决方案:

class Group < ActiveRecord::Base
  has_one :user, autosave: false
  ##OR##
  has_one :usr, class_name: "User"

  # has_one :user, class_name: "User" works weird too
end

但它并不能说服我。

有什么不对?在Rails 4.0一切正常。

更新1

好的,我找到了另外两个解决方案:

class User < ActiveRecord::Base
  belongs_to :group, dependent: :destroy

  validates :login, presence: true

  before_create :add_group

  private

  def add_group
    self.group = Group.create(name: login)
    # OR #
    assign_attributes(group: Group.create(name: login))
  end
end

class Group < ActiveRecord::Base
  has_one :user
end

但我仍然不明白为什么它表现得如此奇怪......特别是这个额外的空记录并由has_one :usr, class_name: "User"修复

2 个答案:

答案 0 :(得分:0)

您只应构建您的论坛,并让AR为您保留:

class User < ActiveRecord::Base
  belongs_to :group, dependent: :destroy

  validates :login, presence: true

  before_create :add_group

  private

  def add_group
    build_group(name: login)
  end
end

调用create_group时,它会再次重新启动AR验证,从而重新启动before_create挂钩。

答案 1 :(得分:0)

答案就在这里:autosave: false

这意味着当您向用户添加组时,rails会将de group_id分配给用户(belongs_to),因此尝试再次保存de object,我认为因为您仍处于创建行为中,所以#save会触发再次使用after_create,你得到的堆栈级别太深了。

改为使用group.create(name: login)