has_oneの関連づけをそのまま更新しようとして "The record failed to save after its foreign key was set to nil." と言われた

# 概要

Engineerモデルと EngineerHiringモデルを has_one , belongs_toで関連づけて、accept_nested_attributes_forでまとめて更新しようとしている、
EngineerのsaveでEngineerHiringまで、保存することは出来たけど、そのupdateでタイトルのエラーが出た

# 原因

正確なエラーメッセージは

ActiveRecord::RecordNotSaved (Failed to remove the existing associated engineer_hiring. The record failed to save after its foreign key was set to nil.):

で、has_oneが指定されていた 更新の際に EngineerHiringのidが指定されていなかったため、関連づけをnullにしようとして上記エラーを出していた。模様です



# 解決方法

has_one :engineer_hirirengをする際に、dependent: :destroy、もしくは accept_nested_atributes_forでupdate_only: trueを宣言する

# 技術的な仕様を確認する

（ https://api.rubyonrails.org/ より)

* has_one(name, scope=nil, **options)
    * 他方のクラスが外部キーを持っているときに利用可能。こちらのクラスの方に外部キーがある場合は belongs_toを使う必要がある
    * scopeを指定して関連づけることも出来る
    * Optionsとして
        * :dependent
            * オーナーが破棄されたときに関連しているオブジェクトに何をするのか定義する
                * :destroy 関連づけられたオブジェクトを破棄する
                * その他 :delete, :nullifyとか
        * その他、:foreing_key、:class_nameとか
* accepts_nested_attributes_for(*attr_names)
    * 関連づけの属性の書き込みについて定義する
    * Optionとして
        * update_only
            * 1対1の関連について、関連づけデータが既に存在しているときにその属性をどの様に使うかを決める。デフォルトは false　で、その場合 :id が指定されている場合は既存のデータが更新される。でなければ別のデータで置き換えられる。trueが設定されている場合は常にupdateされる


# 状況の解説

(詳細は省略して紹介）

## schema

schema.rb

```ruby
  create_table "engineers", comment: "技術者", force: :cascade do |t|
    t.bigint "person_info_id", comment: "技術者個人情報"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["person_info_id"], name: "index_engineers_on_person_info_id"
  end

  create_table "engineer_hirings", comment: "技術者雇用所属(派遣元)", force: :cascade do |t|
    t.bigint "engineer_id"
    t.string "hiring_memo", comment: "技術者雇用メモ"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["engineer_id"], name: "index_engineer_hirings_on_engineer_id"
  end

  create_table "person_infos", force: :cascade do |t|
    t.string "first_name"
    t.string "middle_name"
    t.string "last_name"
    t.string "kana_first_name"
    t.string "kana_middle_name"
    t.string "kana_last_name"
    t.integer "user_status", default: 1, comment: "{0:無効, 1:有効}"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end
```

## model

engineer.rb

```ruby
class Engineer < ApplicationRecord
	has_one :engineer_hiring, autosave: true
	belongs_to :person_info, autosave: true
	accepts_nested_attributes_for :engineer_hiring, update_only: true
	accepts_nested_attributes_for :person_info
	def self.parameters(param_hash,key)
		param_hash.require(key).permit(
			:person_info_attributes => [
				:last_name,:first_name,:middle_name,
				:kana_last_name,:kana_first_name, :kana_middle_name
			],
			:engineer_hiring_attributes => [
				:hiring_memo
			],
		)
	end
```

## controller

engineer_controller.rb

```ruby
	def new
		@engineer=Engineer.new
	end

	def create
		@engineer=Engineer.new
		save_engineer(params)
                redirect_to action: "edit", id: @engineer.id
	end

	def edit
		@engineer = Engineer.find(params[:id])
		render action: "new"
	end

	def update
		@engineer = Engineer.find(params[:id])
		save_engineer(params)
                redirect_to action: "edit", id: params[:id]
	end

	def save_engineer(params)
		Engineer.transaction do
			@engineer.attributes = Engineer.parameters(params, :engineer)
			@engineer.save!
		end
	end

```