【问题标题】:How do I destroy the other has_one relation when updating an association?更新关联时如何销毁另一个 has_one 关系?
【发布时间】:2026-01-28 01:55:01
【问题描述】:

我有一个User,其中有一个Widget

class User
  has_one :widget, :dependent => :destroy
end

class Widget
  belongs_to :user
end

当我为User 创建一个新的Widget 时,我想销毁与User 关联的旧的。

这是我的情况:

创建用户:

user = User.new
user.save
user # => #<User id: 1>

创建用户的小部件:

widget = Widget.new
widget.user = user
widget.save

重新加载并检查小部件:

user.reload
user.widget # => #<Widget id: 1, user_id: 1>

构建一个小部件,注意现有的小部件在另一个被保存之前被销毁:

user.build_widget # => #<Widget id: nil, user_id: 1>
user.reload
user.widget # => nil

重新创建用户的小部件:

user.create_widget # => #<Widget id: 2, user_id: 1>

创建另一个小部件:

widget = Widget.new :user => user
widget.save

现在,两者都存在:

Widget.find(2) # => #<Widget id: 2, user_id: 1>
Widget.find(3) # => #<Widget id: 3, user_id: 1>

用户是第一位的:

user.reload
user.widget # => #<Widget id: 2, user_id: 1>

有没有办法做到这一点:

def create
  @widget = current_user.build_widget(params[:widget])

  respond_to do |format|
    if @widget.save
      format.html { redirect_to widget_path, notice: 'Widget was successfully created.' }
      format.json { render json: @widget, status: :created, location: @widget }
    else
      format.html { render action: 'new' }
      format.json { render json: @widget.errors, status: :unprocessable_entity }
    end
  end
end

在保存之前不删除旧的小部件,或者这样:

def create
  @widget = Widget.new(params[:widget])
  @widget.user = current_user

  respond_to do |format|
    if @widget.save
      format.html { redirect_to widget_path, notice: 'Widget was successfully created.' }
      format.json { render json: @widget, status: :created, location: @widget }
   else
      format.html { render action: 'new' }
      format.json { render json: @widget.errors, status: :unprocessable_entity }
    end
  end

结束

不保留两份副本?

我不想用像这样的交易弄乱我的控制器

Widget.transaction do
  old_widget.destroy
  new_widget.save
end

但到目前为止,这似乎是唯一的方法。

【问题讨论】:

    标签: ruby-on-rails transactions controller


    【解决方案1】:

    看起来您有两个用户可以创建小部件的使用路径。从用户端和小部件端。如果您通过一段代码将它们汇集起来会更好,并进行一些唯一性验证以确保没有失误。

    如何在 user.create_widget 中使用 find_or_create_by 以便在需要更新或创建新小部件时编辑现有小部件。

    【讨论】:

      【解决方案2】:

      您应该为user 更新现有的widget 记录,而不是创建新记录并销毁旧记录。 你可以这样做:

      class User
        has_one :widget, :dependent => :destroy
      
        def assign_widget(attr_hash)
          widget ? widget.update(attr_hash) : widget.create(attr_hash)
          widget.reload
        end
      end
      

      【讨论】:

        最近更新 更多