概要
- スキルアップのため、レシピ投稿サイトを作っていました(cookpadのようなもの)。
- 一つのレシピにつき、複数の写真を投稿できるようにしたかった。
- 以下の記事を参考に、form_for + fields_forで、recipeレコードが保存されると、それに関連したimageレコードも保存されるようにした。
複数のレコードを作成する。modelの関係性によって異なるform_for / fields_forの使い方
Rails 複数の子レコードの作成・更新を自在に扱う (accepts_nested_attributes_for)
Railsで階層化された複数モデルに対応するフォームの作り方
上記サイトのコピーで、書いたコード
- コントローラー
recipes_controller.rb
class RecipesController < ApplicationController
def new
@recipe = Recipe.new
@recipe.images.build
end
def create
recipe = current_user.recipes.new(recipe_params)
recipe.save
redirect_to root_path
end
private
def recipe_params
params.require(:recipe).permit(:name, images_attributes: [:image])
end
end
- ビュー
new.html.erb
<%= form_for @recipe do |f| %>
<%= f.text_field :name %>
<%= f.fields_for :images do |i| %>
<%= i.file_field :image %>
<% end %>
<%= f.submit "Save",name:"commit" %>
<% end %>
- imageモデル
image.rb
class Image < ApplicationRecord
belongs_to :recipe
end
- recipeモデル
recipe.rb
class Recipe < ApplicationRecord
belongs_to :user
has_many :images
accepts_nested_attributes_for :images
end
しかし、これだと保存されない!!
- 結論から言うと、imageモデルの記述を、belongs_to :recipe,optional: trueにすると無事に保存されました!!
- なぜ、optional: trueがないと、ダメかと言うと、rails5から、belongs_toに対して、required:trueがデフォルトで設定されているからみたいです。
required:trueとは
- (私の理解が正しければ)値がnilだと保存されないよ!というものです。
- つまり、imageモデルにはbelongs_to :recipeしか書いてなくとも、実際はbelongs_to :recipe,required:trueとなっているということです。
rails5はデフォルトでは、外部キー(hoge_id)のnilは許させない!!
- DBに保存されるまでは、idはnilなので、つまり、imageモデルでの時点では、recipe_id(imageレコードの外部キー)はnilなので、
required:trueのバリデーションに引っかかり、保存されなかったということみたいです!!
optional: trueはrequired:trueを無効化する!!
- これで無事に保存されました!!