今回やりたいこと
一度の投稿で複数のモデルにデータを保存したい!
背景
例えばツイッターなどで1つの投稿につき画像が複数保存される場合、image1、image2、image3・・・というカラムを作らなくてはいけなくなるため、投稿と紐ついたimageテーブルを作ることで解決しようとした。
## 実装方法
①tweetモデルとimageモデルを作る
②tweetモデルを親モデルとし、imageモデルと紐つける
③viewでfields_forを使ってform_forのなかで子モデルに対してもデータが保存できるようにする
④controllerで_attibutesをつかってitemのparamsの中で一括で受け取るように記述
controller
class TweetsController < ApplicationController
def new
@tweet = Tweet.new
@tweet.images.build
end
def create
Tweet.create(tweet_params)
end
private
def tweet_params
params.require(:tweet).permit(
:text,
images_attributes: {image: []}
)
end
end
model
親モデル
class Tweet < ApplicationRecord
has_many :images, inverse_of: :tweet
accepts_nested_attributes_for :images
end
子モデル
class Image < ApplicationRecord
belongs_to :tweet, inverse_of: :images
mount_uploaders :image, ImageUploader
end
view
.contents
= form_for @tweet do |f|
= f.label :text, "テキスト"
= f.text_area :text, placeholder: "ここに文字を入力してね!"
.file
= f.fields_for :images do |c|
= c.label :image
= c.file_field :image, multiple: true
.submit
= f.submit "投稿", {class: "btn"}
詰まったところ
paramsは送られているのにimageに保存ができない!(rollbackやエラーはなし)
理由:controllerの_attributesの書き方がわるい
誤
params.require(:tweet).permit(
:text,
images_attributes: [:image]
)
正
params.require(:tweet).permit(
:text,
images_attributes: {image: []}
)
これだと一枚の場合は保存できるが、複数枚送られたときに保存ができない。
画像を保存しようとすると「no implicit conversion of nil into String」とエラーが出る。
直訳するとnilはStringに変えれませんよ!と怒られている。
paramsにnilが入ってるのかなと思って何度もみてもnilは見つからなかった。
ググってるうちに以下の記事が救ってくれた。
・“no implicit conversion of nil into String” error for carrierwave multiple image upload
https://stackoverflow.com/questions/33401629/no-implicit-conversion-of-nil-into-string-error-for-carrierwave-multiple-image
複数画像の時はmount_uploder"s"にしないといけなかったっぽい
一枚→ mount_uploader :image, ImageUploader
複数→ mount_uploaders :image, ImageUploader
まとめ
新しい機能を実装するときにはミニアプリを使って挙動確かめるのがおすすめ。