【Rails5】1つのフォームで複数のモデルにデータを保存
解決済
回答 1
投稿
- 評価
- クリップ 0
- VIEW 4,229
動作環境
ruby 2.4.4
Rails 5.2.0
gem 2.6.14.1
carrierwave 1.2.3
現在行っていること・問題
・Rails5を用いて、複数のモデルにデータを保存するフォームを作成中です。
・SirabasuというモデルとImageモデルがあり、1対多の関係を持っています。
・Sirabasuにレコードを1つ保存するフォーム内において、画像アップロードのfieldを記述し、その画像はImageに保存されるようにしたいです。(現状、画像は1枚としていますが、いずれは複数登録したい)
・Sirabasuにはデータが保存されているのですが、Imageには保存されませんでした。
調べた限りでの対策は行ったつもりなのですが、どうして良いかわかりません。どなたか解決策をご教授いただけないでしょうか。
参考にしたサイト
【Rails】formで複数モデル(テーブル)にレコードを登録 - teratail
一つのform_forで複数のテーブルに保存!! - Qiita
schema(一部)
create_table "images", force: :cascade do |t|
t.string "image_path"
t.integer "sirabasu_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "sirabasus", force: :cascade do |t|
t.string "name"
t.text "content"
t.integer "number"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "userid"
t.integer "cid"
end
Model(Sirabasu)
class Sirabasu < ApplicationRecord
has_many :images, dependent: :destroy
accepts_nested_attributes_for :images
end
Model(Image)
class Image < ApplicationRecord
belongs_to :sirabasu, optional: true
end
def new
@sirabasu = Sirabasu.new
@sirabasu.images.build
end
def create
@sirabasu = Sirabasu.new(sirabasu_params)
if @sirabasu.save
redirect_to('/sirabasus')
else
render 'new'
end
end
def sirabasu_params
params.require(:sirabasu).permit(:number, :name, :content, :userid, :cid, sirabasus_attributes: [:image_path])
end
View(sirabasu#new)一部
<%= form_for(@sirabasu) do |form| %>
<%= form.number_field :number %>
<%= form.text_field :name %>
<%= form.text_area :content %>
<%= form.fields_for :images do |i| %>
<%= i.file_field :image_path %>
<% end %>
<%= form.hidden_field :cid, :value => "" %>
<%= form.hidden_field :userid, :value => "" %>
<%= form.submit "登録", :class =>"link-button"%>
<% end %>
-
気になる質問をクリップする
クリップした質問は、後からいつでもマイページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
クリップを取り消します
-
良い質問の評価を上げる
以下のような質問は評価を上げましょう
- 質問内容が明確
- 自分も答えを知りたい
- 質問者以外のユーザにも役立つ
評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。
質問の評価を上げたことを取り消します
-
評価を下げられる数の上限に達しました
評価を下げることができません
- 1日5回まで評価を下げられます
- 1日に1ユーザに対して2回まで評価を下げられます
質問の評価を下げる
teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。
- プログラミングに関係のない質問
- やってほしいことだけを記載した丸投げの質問
- 問題・課題が含まれていない質問
- 意図的に内容が抹消された質問
- 過去に投稿した質問と同じ内容の質問
- 広告と受け取られるような投稿
評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。
質問の評価を下げたことを取り消します
この機能は開放されていません
評価を下げる条件を満たしてません
質問の評価を下げる機能の利用条件
この機能を利用するためには、以下の事項を行う必要があります。
- 質問回答など一定の行動
-
メールアドレスの認証
メールアドレスの認証
-
質問評価に関するヘルプページの閲覧
質問評価に関するヘルプページの閲覧
checkベストアンサー
+2
formオブジェクトというデザインパターンを使うのがいいかなと思います。フォームにActiveRecordのモデルを渡すのではなく保存に関する要件をまとめた新しいクラスを渡します。
SirabasuFormという保存のためのクラスを新たに用意します。SirabasuFormの中でSirabasuとImageのそれぞれのモデルを保存するようにします。
Controller
def new
@sirabasu_form = SirabasuForm.new #formオブジェクトパターンのためのクラスをviewでformに渡します
end
def create
@sirabasu_form = SirabasuForm.new(sirabasu_params)
if @sirabasu_form.save
redirect_to('/sirabasus')
else
render 'new'
end
end
def sirabasu_params
params.require(:sirabasu_form).permit(:number, :name, :content, :userid, :cid, :image_path)
end
Model
class Sirabasu < ApplicationRecord
has_many :images, dependent: :destroy
end
class Image < ApplicationRecord
belongs_to :sirabasu, optional: true
end
class SirabasuForm
include ActiveModel::Model #テーブルは持ちませんがApplicationRecordのsaveメソッドなどを提供します
attr_accessor :number, :name, :content, :userid, :cid, :image_path
validates :name, :content, presence: true
def save
return false if invalid? #バリデーションを追加する場合はここに追加します
sirabasu = Sirabusu.new(name: name, content: content, number: number, userid: userid, cid: cid)
sirabusu.images.new(image_path: image_path) #この処理をimageの分だけループすれば複数保存も出ます
sirabusu.save
end
end
View
<%= form_with(model: @sirabasu_form, url: sirabasu_path) do |form| %>
<%= form.number_field :number %>
<%= form.text_field :name %>
<%= form.text_area :content %>
<%= form.file_field :image_path %>
<%= form.hidden_field :cid, :value => "" %>
<%= form.hidden_field :userid, :value => "" %>
<%= form.submit "登録", :class =>"link-button"%>
<% end %>
hidden_fieldで入力を求めない値はコントローラで受け渡してもいいかもしれません
def create
@sirabasu_form = SirabasuForm.new(sirabasu_params.merge(cid: ***, userid: ***))
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
15分調べてもわからないことは、teratailで質問しよう!
- ただいまの回答率 88.34%
- 質問をまとめることで、思考を整理して素早く解決
- テンプレート機能で、簡単に質問をまとめられる
2018/11/08 01:41
formオブジェクトやform_withは全く知らなかったので、参考になりました。複数の画像をアップロードする機能にはまだ挑戦していませんが、質問の件については解決したので、ひとまず安心です。ありがとうございました。