ActiveRecord::RecordNotSaved - Failed to save the new associated xxx.:が発生したときの解決法

ActiveRecord::RecordNotSaved - Failed to save the new associated モデル名.:

が発生し解決に少し時間かかったのと、

エラーでググっても同じ問題で発生している人が見当たらなかったので、メモしておきます

結論

とりあえず結論

関連テーブルをnewで作成しつつ代入し用途した際に、newで作成失敗しエラーが発生したみたいです

これだけだと分かりづらいので説明していきます

ソースを使って詳しく解説

まずは前提

Blogというモデルがあり、それに対して1対1で紐づくOwnerがあったとします Blog作成後にのupdateでBlogに紐づくOwnerを作成して一緒に紐付けを行うという処理になります

class Blog < ApplicationRecord
  has_one: owner

end

class Owner < ApplicationRecord
  belongs_to: blog

end

class BlogsController < ApplicationController

  def update
    @blog = Blog.find(params[:id])
    if !@blog.owner
      @blog.owner = Owner.new(
        name: params[:name],
        email: params[:email]
      )
    end
  end

end

仕様追加で処理を追加

仕様が追加され、新しくCompanyテーブルを作成しOwnerを紐付けました

class Blog < ApplicationRecord
  has_one: owner, dependent: :destroy

end

class Owner < ApplicationRecord
  belongs_to: blog
  belongs_to: company # 新しく追加

end

# 新しくCompanyモデルを追加
class Company < ApplicationRecord
  has_many: owner

end

class BlogsController < ApplicationController

  def update
    @blog = Blog.find(params[:id])
    if !@blog.owner
      @blog.owner = Owner.new(
        name: params[:name],
        email: params[:email]
      )
    end
  end

end

エラー発生

この状態で動かしたら以下のようなエラーが出ました

ActiveRecord::RecordNotSaved - Failed to save the new associated owner.:

@blog.owner = Owner.new(でエラーが出ているということは、エラーの詳細内容で分かったのですがパッと原因が分からず

なので一旦newして変数に入れてから紐付けを試してみたり、newする時の渡す値を変えたり変更したりと試している中でやっと原因らしき情報を手に入れました

Owner.new(だったのをOwner.create!(に変更したときに、comanyを入力してくださいという、エラーが発生

原因判明

原因はOwner.newする際に一緒にcompanyを渡していないことが原因でした

先日書いた以下の記事で説明しているのですが、

belongs_to: company, optional: trueであれば、companyをしていなくてもエラーにはならないのですが、

今回は必ず設定して欲しかったのでoptional: trueは設定していませんでした

tchkmch.hatenablog.com

Owner.newでエラーが発生し、それが原因で@blog.owner = Owner.new(にてエラーになっていたようです

以下のようにcompanyを設定してあげるようにしたら無事動くようになりました!

class BlogsController < ApplicationController

  def update
    @blog = Blog.find(params[:id])
    company = Company.find_by(params[:comapny_id])
    if !@blog.owner
      @blog.owner = Owner.new(
        name: params[:name],
        email: params[:email],
        company: company # OwnerにCompanyへの紐付けを追加
      )
    end
  end

end

最後に

ちょっとしたことでハマることってたまにあるんですよね

同じようなケースがググっても見つからなかったのでハマる人は少ないのかもしれませんが、同じようにハマった人の参考になれば幸いです