【问题标题】:Using `assign_attributes` saves `has_many through:` association immediately使用 `assign_attributes` 立即保存 `has_many through:` 关联
【发布时间】:2017-06-29 07:57:27
【问题描述】:

据我所知,assign_attributes(与 update_attributes 不同)不应该保存记录或任何记录。

所以当我发现在为 has_many through: 关系提供 _ids 时这不是真的时,我感到非常震惊。

考虑以下示例:

class GroupUser < ApplicationRecord
  belongs_to :group
  belongs_to :user
end

class Group < ApplicationRecord
  has_many :group_users
  has_many :users, through: :group_users
end

class User < ApplicationRecord
  has_many :group_users
  has_many :groups, through: :group_users

  validates :username, presence: true
end

因此,我们的用户和组处于 m-to-m 关系中。

Group.create # Create group with ID 1
Group.create # Create group with ID 2

u = User.create(username: 'Johny')

# The following line inserts two `GroupUser` join objects, despite the fact 
# that we have called `assign_attributes` instead of `update_attributes` 
# and, equally disturbing, the user object is not even valid as we've 
# supplied an empty `username` attribute.
u.assign_attributes(username: '', group_ids: [1, 26])

评论者要求的日志:

irb(main):013:0> u.assign_attributes(username: '', group_ids: [1, 2])
  Group Load (0.2ms)  SELECT "groups".* FROM "groups" WHERE "groups"."id" IN (1, 2)
  Group Load (0.1ms)  SELECT "groups".* FROM "groups" INNER JOIN "group_users" ON "groups"."id" = "group_users"."group_id" WHERE "group_users"."user_id" = ?  [["user_id", 1]]
   (0.0ms)  begin transaction
  SQL (0.3ms)  INSERT INTO "group_users" ("group_id", "user_id", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["group_id", 1], ["user_id", 1], ["created_at", "2017-06-29 08:15:11.691941"], ["updated_at", "2017-06-29 08:15:11.691941"]]
  SQL (0.1ms)  INSERT INTO "group_users" ("group_id", "user_id", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["group_id", 2], ["user_id", 1], ["created_at", "2017-06-29 08:15:11.693984"], ["updated_at", "2017-06-29 08:15:11.693984"]]
   (2.5ms)  commit transaction
=> nil

我敢说update_attributes_ids 构造主要用于处理Web 表单——在本例中是更新用户本身及其组关联的表单。所以我认为可以肯定地说这里的一般假设是全有或全无,而不是部分保存。

我在某些方面使用错了吗?

【问题讨论】:

  • 是什么让你说记录被保存了?不是,试试u.valid?u.save!
  • 我可以看到查询并且它保存在数据库中的事实(对于连接表,当然不是记录本身)。 valid?falsesave! 正如预期的那样引发验证错误。
  • 您看到查询了吗?你能验证一下GroupUser.pluck(:group_id)u.reload!u.username吗?
  • 当然不会保存用户对象,只保存关联(这就是这个问题的重点)。我向你保证,情况就是这样......我能够验证数据库中是否有连接记录;)

标签: ruby-on-rails activerecord ruby-on-rails-5 activemodel


【解决方案1】:

@gokul-m 建议在https://github.com/rails/rails/issues/17368 阅读有关该问题的信息。其中一个 cmets 指出了一个临时解决方法:https://gist.github.com/remofritzsche/4204e399e547ff7e3afdd0d89a5aaf3e

【讨论】:

  • 是的,这就是我最终想出的解决方案。但我仍然希望 Rails 解决这个问题 - 在我看来,当前的行为是如此出乎意料,以至于我会将其视为一个错误。
【解决方案2】:

我解决这个问题的一个例子:

  ruby: 
  def assign_parameters(attributes, options = {})
    with_transaction_returning_status {self.assign_attributes(attributes, options)}
  end

【讨论】:

  • 请始终将您的答案放在上下文中,而不仅仅是粘贴代码。有关详细信息,请参阅here
【解决方案3】:

您可以像这样使用 assign_attributes 处理验证

@item.assign_attributes{ year: "2021", type: "bad" }.valid?

【讨论】:

  • assign_attributes 的调用已经保留了更改,所以恐怕这不是一个解决方案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-12-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-17
相关资源
最近更新 更多