11
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

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

Last updated at Posted at 2018-09-18

概要

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

  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

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

	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

11
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?