質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
Ruby on Rails 5

Ruby on Rails 5は、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

Model

MVCモデルの一部であるModelはアプリケーションで扱うデータとその動作を管理するために扱います。

Q&A

解決済

1回答

1751閲覧

親モデル(user)に紐づく(has_many)子モデル(step)を一括保存をしたい

White_fox

総合スコア6

Ruby on Rails 5

Ruby on Rails 5は、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

Model

MVCモデルの一部であるModelはアプリケーションで扱うデータとその動作を管理するために扱います。

0グッド

0クリップ

投稿2020/08/31 14:22

編集2020/09/02 10:34

解決したいこと:userモデルに紐づくstepモデルを一括保存をしたい

Railsで一括保存する機能を開発しています。
モデルを一括登録する手順を参考にしてやってみましたが

どのようにすれば一括保存をすることができますか?
色々試しましたがどうしても解決できませんでした。
足りない情報があれば指摘していただけると助かりますm(_ _)m

問題が発生するまでの流れ・手順
new.html.erbでフォームに入力して登録ボタンを押した時

イメージ説明

※追記(最新の情報に修正)
app/views/steps/new.html.erb

<%= form_with model: @steps, url: steps_path, local: true do |form| %> <% @steps.collection.each do |step| %> <%= fields_for 'steps[]', step do |field| %> <%= field.label :step_name %> <%= field.text_field :step_name, class: 'form-field' %> <br> <% end %> <% end %> <%= form.submit %> <%= link_to '戻る', step_path(current_user) %> <% end %>

app/models/user.rb

#== Schema Information # id :bigint not null, primary key # admin :boolean default(FALSE) # email :string(255) # name :string(255) # password_digest :string(255) # remember_digest :string(255) # created_at :datetime not null # updated_at :datetime not null # # Indexes # # index_users_on_email (email) UNIQUE # class User < ApplicationRecord has_many :steps, dependent: :destroy end

app/models/step.rb

# == Schema Information # # Table name: steps # # id :bigint not null, primary key # goal :string(255) # step_day :date # step_name :text(65535) # step_time :integer # created_at :datetime not null # updated_at :datetime not null # user_id :bigint # # Indexes # # index_steps_on_user_id (user_id) # # Foreign Keys # # fk_rails_... (user_id => users.id) # class Step < ApplicationRecord belongs_to :user end

app/models/steps_collection.rb

# Stepのコレクションモデル class StepCollection include ActiveModel::Conversion extend ActiveModel::Naming extend ActiveModel::Translation include ActiveModel::AttributeMethods include ActiveModel::Validations STEP_NUM = 3 # 同時にstepを作成する数 attr_accessor :collection # 初期化メソッド def initialize(current_user,attributes = []) if attributes.present? self.collection = attributes.map do |value| # 修正 Step.new( user_id: value['current_user.id'], step_name: value['step_name'] ) end else self.collection = STEP_NUM.times.map { Step.new } end end # レコードが存在するか確認するメソッド def persisted? false end # コレクションをDBに保存するメソッド def save is_success = true ActiveRecord::Base.transaction do collection.each do |result| # バリデーションを全てかけたいからsave!ではなくsaveを使用 is_success = false unless result.save end # バリデーションエラーがあった時は例外を発生させてロールバックさせる raise ActiveRecord::RecordInvalid unless is_success end rescue p 'エラー' ensure return is_success end end

app/controllers/steps_controller.rb

class StepsController < ApplicationController def new @steps = StepCollection.new(current_user) end def create @steps = StepCollection.new(current_user, step_params) if @steps.save flash[:success] = '成功しました!' redirect_to step_path(current_user) else render 'new' end end private def step_params params.require(:steps) end

app/helpers/sessions_helpers.rb

module SessionsHelper # 渡されたユーザーでログインする def log_in(user) session[:user_id] = user.id end # ユーザーのセッションを永続的にする def remember(user) user.remember cookies.permanent.signed[:user_id] = user.id cookies.permanent[:remember_token] = user.remember_token end # 記憶トークンcookieに対応するユーザーを返す def current_user if (user_id = session[:user_id]) @current_user ||= User.find_by(id: user_id) elsif (user_id = cookies.signed[:user_id]) user = User.find_by(id: user_id) if user && user.authenticated?(cookies[:remember_token]) log_in user @current_user = user end end end # 渡されたユーザーがログイン済みユーザーであればtrueを返す def current_user?(user) user == current_user end # ユーザーがログインしていればtrue、その他ならfalseを返す def logged_in? !current_user.nil? end # 永続的セッションを破棄する def forget(user) user.forget cookies.delete(:user_id) cookies.delete(:remember_token) end # 現在のユーザーをログアウトする def log_out forget(current_user) session.delete(:user_id) @current_user = nil end # 記憶したURL (もしくはデフォルト値) にリダイレクト(フレンドリーフォワーディング) def redirect_back_or(default) redirect_to(session[:forwarding_url] || default) session.delete(:forwarding_url) end # アクセスしようとしたURLを覚えておく def store_location session[:forwarding_url] = request.original_url if request.get? end end

ログ

Started POST "/steps" for 172.19.0.1 at 2020-09-01 13:31:37 +0000 web_1 | Cannot render console from 172.19.0.1! Allowed networks: 172.18.0.1, 127.0.0.0/127.255.255.255, ::1 web_1 | Processing by StepsController#create as HTML web_1 | Parameters: {"utf8"=>"✓", "authenticity_token"=>"pZy8W+7yYOGz+BNc8CfWRJW+rBlkVX1ctV5OS7DWHFCe6FTD4ixvm97mVTw0kJ9v3naRHGnFREDo/r+2aM/x5Q==", "steps"=>[{"step_name"=>"step"}, {"step_name"=>"step1"}, {"step_name"=>"step2"}], "commit"=>"登録する"} web_1 | User Load (0.3ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 8 LIMIT 1 web_1 | (0.3ms) BEGIN web_1 | (0.3ms) ROLLBACK web_1 | "エラー" web_1 | Rendering steps/new.html.erb within layouts/application web_1 | Rendered steps/_form.html.erb (2.3ms) web_1 | Rendered steps/new.html.erb within layouts/application (14.7ms) web_1 | Rendered layouts/_shim.html.erb (0.4ms) web_1 | Rendered layouts/_head.html.erb (441.1ms) web_1 | Rendered layouts/_header.html.erb (2.3ms) web_1 | Rendered layouts/_flash_messages.html.erb (0.4ms) web_1 | Rendered layouts/_footer.html.erb (2.0ms) web_1 | Completed 200 OK in 644ms (Views: 563.5ms | ActiveRecord: 3.0ms)

環境

rails: 5.2.3 ruby: 2.5.7

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

同じ名前の変数があって混乱しているのかも。
newなので対象はひとつですから @steps を @step にしてcontroller、viewをつくりなおしてみてください。

あ!

もしかして私、盛大な勘違いしているかも。
User に has_manyされている steps の一括登録ですよね?
参照先は 関連とは関係なく Userそのものを一括登録なので、そのままではまずいかも。

参照をきちんと写経していないですね。
参照では params.require(:users) でpermitはないです。
そのままだと strongparamaterで引っかかるので、collectionのほうで項目ごとに一つ一つ設定しています。

で、参照のままではまずいのは、Stepがbelongs_to :userですから、step作成時に user_id を与えないとvlidationエラーとなります。

追記

すみません①は私の勘違いでしたので、やらなくても良かったです。
③のエラーですが一時的に
model Stepのschemaが分からないので推定ですが、持っているのは user_id と step_name だけではないですか?なのに id: value['id}'], user_id: value['user_id'], step_day: value['step_day'], step_name: value['step_name'], created_at: value['created_at'], updated_at: value['updated_at'], goal: value['goal']
とないものまで登録していることによるエラーです。

追記2
StepCollection の def initialize ですが
Step.new( user_id: value['current_user.id'],
のままですか?
user_id: current_user.id,
にしてください。

投稿2020/08/31 22:49

編集2020/09/03 00:53
winterboum

総合スコア23567

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

White_fox

2020/09/02 08:49 編集

回答ありがとうございます。 ①steps_controller を以下のように変更しました。(@stepsを@stepに変更,params.permit(:users)に変更) def new @step = StepCollection.new end def create @step = StepCollection.new(step_params) if @step.save flash[:success] = '成功しました!' redirect_to step_path(current_user) else render 'new' end end private def step_params params.permit(:users) end ビューを以下のように変更しました。(@stepsを@stepに変更,'steps[]'を'step[]'に変更) <%= form_with model: @step, url: steps_path, local: true do |form| %> <% @step.collection.each do |step| %> <%= fields_for 'step[]', step do |field| %> <%= field.label :step_name %> <%= field.text_field :step_name , class: 'form-field' %> <br> <% end %> <% end %> <%= form.submit %> <%= link_to '戻る', step_path(current_user) %> <% end %> </div> ②に続く
White_fox

2020/09/01 04:12

②steps_collection.rbを修正しましたStep.new()の部分を追加 # 初期化メソッド def initialize(attributes = []) if attributes.present? self.collection = attributes.map do |value|    # この部分を修正 Step.new( id: value['id}'], user_id: value['user_id'], step_day: value['step_day'], step_name: value['step_name'], created_at: value['created_at'], updated_at: value['updated_at'], goal: value['goal'] ) end else self.collection = STEP_NUM.times.map{ Step.new } end end 登録できるか試したところ、rollbackしてしまいます。 ③に続く
White_fox

2020/09/01 04:18

ログを見たところ Started POST "/steps" for 172.24.0.1 at 2020-09-01 04:10:42 +0000 web_1 | Cannot render console from 172.24.0.1! Allowed networks: 172.18.0.1, 127.0.0.0/127.255.255.255, ::1 web_1 | Processing by StepsController#create as HTML web_1 | Parameters: {"utf8"=>"✓", "authenticity_token"=>"xZbHFSydxWz8g2msa5jZ2yvWkDeGNrMY+z1vjgDRInQCQb4CW5VDe0XoIAUJ7dGFgjFFk49IxvZLamxKhamD2g==", "step"=>[{"step_name"=>"test"}, {"step_name"=>"test2"}, {"step_name"=>"test3"}], "commit"=>"登録する"} web_1 | User Load (0.4ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 8 LIMIT 1 web_1 | Unpermitted parameters: :utf8, :authenticity_token, :step, :commit web_1 | (0.3ms) BEGIN web_1 | (0.3ms) ROLLBACK web_1 | "エラー" web_1 | Rendering steps/new.html.erb within layouts/application web_1 | Rendered steps/_form.html.erb (3.7ms) web_1 | Rendered steps/new.html.erb within layouts/application (20.1ms) web_1 | Rendered layouts/_shim.html.erb (0.3ms) web_1 | Rendered layouts/_head.html.erb (322.9ms) web_1 | Rendered layouts/_header.html.erb (1.5ms) web_1 | Rendered layouts/_flash_messages.html.erb (0.3ms) web_1 | Rendered layouts/_footer.html.erb (1.3ms) web_1 | Completed 200 OK in 767ms (Views: 663.7ms | ActiveRecord: 3.4ms) このような表示が出ています。どの部分を修正したら良いでしょうか?
White_fox

2020/09/01 10:27 編集

①の部分をもとに戻しました(@stepを@stepsに修正) def new @steps = StepCollection.new end def create @steps = StepCollection.new(step_params) if @steps.save flash[:success] = '成功しました!' redirect_to step_path(current_user) else render 'new' end end def step_params params.require(:steps) end ビューの部分の修正(@stepを@stepsに変更,'step[]'を'steps[]'に修正) <%= form_with model: @steps, url: steps_path, local: true do |form| %> <% @steps.collection.each do |step| %> <%= fields_for 'steps[]', step do |field| %> <%= field.label :step_name %> <%= field.text_field :step_name , class: 'form-field' %> <br> <% end %> <% end %> <%= form.submit %> <%= link_to '戻る', step_path(current_user) %> <% end %> </div>
White_fox

2020/09/01 10:32

steps_collection.rbを修正しました def initialize(attributes = []) if attributes.present? self.collection = attributes.map do |value| # 修正 Step.new( user_id: value['user_id'], step_name: value['step_name'] ) end else self.collection = STEP_NUM.times.map { Step.new } end end 登録できるか試したところ、ロールバックが発生しします。 web_1 | Started POST "/steps" for 172.24.0.1 at 2020-09-01 10:29:19 +0000 web_1 | Cannot render console from 172.24.0.1! Allowed networks: 172.18.0.1, 127.0.0.0/127.255.255.255, ::1 web_1 | Processing by StepsController#create as HTML web_1 | Parameters: {"utf8"=>"✓", "authenticity_token"=>"5AS0EPG9ZL9rMrK21UmSbPsUT5Ploqab/jNBm6Dxvr/fcFyI/WNrxQYs9NYR/ttHsNxylugyn4ejk7BmeOhTCg==", "steps"=>[{"step_name"=>"step1"}, {"step_name"=>"step2"}, {"step_name"=>"sep3"}], "commit"=>"登録する"} web_1 | User Load (0.4ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 8 LIMIT 1 web_1 | (0.3ms) BEGIN web_1 | (0.4ms) ROLLBACK web_1 | "エラー" web_1 | Rendering steps/new.html.erb within layouts/application web_1 | Rendered steps/_form.html.erb (2.5ms) web_1 | Rendered steps/new.html.erb within layouts/application (29.6ms) web_1 | Rendered layouts/_shim.html.erb (0.7ms) web_1 | Rendered layouts/_head.html.erb (426.4ms) web_1 | Rendered layouts/_header.html.erb (1.9ms) web_1 | Rendered layouts/_flash_messages.html.erb (0.5ms) web_1 | Rendered layouts/_footer.html.erb (2.5ms) web_1 | Completed 200 OK in 779ms (Views: 615.6ms | ActiveRecord: 5.3ms) どう修正したら良いでしょうか?
winterboum

2020/09/01 11:16

②のnew()の中を、user_id と step_name だけにしてください。 ただし、current_user は引数で渡さないと、model側では取得できないので注意
White_fox

2020/09/01 12:13

初歩的な質問で申し訳ないのですがこういうことで良いのでしょうか?申し訳ないです。 def initialize(attributes = []) if attributes.present? self.collection = attributes.map do |value| # 修正 Step.new( user_id: value['current_user'], step_name: value['step_name'], ) end else self.collection = STEP_NUM.times.map { Step.new } end end
winterboum

2020/09/01 12:28

形としては良いですが、attributes に current_user はないでしょう? def initialize(current_user,attributes = []) として、これを呼ぶcontrollerの方も、current_userを渡すようにしてください。 なお、user_id: current_user  ではなく user_id: current_user.id に
White_fox

2020/09/01 13:17 編集

①指摘されたcurrent_userとcurrent_user.idを追加しました。 def initialize(current_user,attributes = []) if attributes.present? self.collection = attributes.map do |value| # 修正 Step.new( user_id: value['current_user.id'], step_name: value['step_name'] ) end else self.collection = STEP_NUM.times.map { Step.new } end end ②コントローラーにcurrent_userを追加しました。 def new @steps = StepCollection.new(current_user) end def create @steps = StepCollection.new(step_params,current_user) if @steps.save flash[:success] = '成功しました!' redirect_to step_path(current_user) else render 'new' end end private def step_params params.require(:steps) end そこで登録できるか試したところ以下のようなエラーが生じました・・・ 何か間違っているでしょうか? web_1 | Parameters: {"utf8"=>"✓", "authenticity_token"=>"TYMC1MfN4x9f18QAIUntF4CPhhAj0acYUD8nrtNueOJ29+pMyxPsZTLJgmDl/qQ8y0e7FS5BngQNn9ZTC3eVVw==", "steps"=>[{"step_name"=>"step"}, {"step_name"=>"step1"}, {"step_name"=>"step2"}], "commit"=>"登録する"} web_1 | User Load (0.5ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 8 LIMIT 1 web_1 | Completed 500 Internal Server Error in 56ms (ActiveRecord: 2.1ms) web_1 | NoMethodError (undefined method `map' for #<User:0x00007f4a4e88ff28> web_1 | Did you mean? tap): web_1 | web_1 | app/models/steps_collection.rb:14:in `initialize' web_1 | app/controllers/steps_controller.rb:40:in `new' web_1 | app/controllers/steps_controller.rb:40:in `create'
winterboum

2020/09/01 13:19

def initialize(current_user,attributes = []) で待ってるのに StepCollection.new(step_params,current_user) だから。
White_fox

2020/09/02 08:48 編集

①createを修正(@steps = StepCollection.new(current_user,step_params)) <code>def new @steps = StepCollection.new(current_user) end def create @steps = StepCollection.new(current_user,step_params) if @steps.save flash[:success] = '成功しました!' redirect_to step_path(current_user) else render 'new' end end そこで登録できるか試したところロールバックします。 何か間違っているでしょうか? Started POST "/steps" for 172.19.0.1 at 2020-09-01 13:31:37 +0000 web_1 | Cannot render console from 172.19.0.1! Allowed networks: 172.18.0.1, 127.0.0.0/127.255.255.255, ::1 web_1 | Processing by StepsController#create as HTML web_1 | Parameters: {"utf8"=>"✓", "authenticity_token"=>"pZy8W+7yYOGz+BNc8CfWRJW+rBlkVX1ctV5OS7DWHFCe6FTD4ixvm97mVTw0kJ9v3naRHGnFREDo/r+2aM/x5Q==", "steps"=>[{"step_name"=>"step"}, {"step_name"=>"step1"}, {"step_name"=>"step2"}], "commit"=>"登録する"} web_1 | User Load (0.3ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 8 LIMIT 1 web_1 | (0.3ms) BEGIN web_1 | (0.3ms) ROLLBACK web_1 | "エラー" web_1 | Rendering steps/new.html.erb within layouts/application web_1 | Rendered steps/_form.html.erb (2.3ms) web_1 | Rendered steps/new.html.erb within layouts/application (14.7ms) web_1 | Rendered layouts/_shim.html.erb (0.4ms) web_1 | Rendered layouts/_head.html.erb (441.1ms) web_1 | Rendered layouts/_header.html.erb (2.3ms) web_1 | Rendered layouts/_flash_messages.html.erb (0.4ms) web_1 | Rendered layouts/_footer.html.erb (2.0ms) web_1 | Completed 200 OK in 644ms (Views: 563.5ms | ActiveRecord: 3.0ms)
winterboum

2020/09/01 20:52

code読みにくくなってきたので、質問欄に最新版を<code>つかって追記してください。Post controllerとStepCollection
White_fox

2020/09/02 09:03

質問欄を最新版に修正しました。
White_fox

2020/09/03 10:24

StepCollection の def initializeを Step.new( user_id: value['current_user.id'], から Step.new( user_id: current_user.id, に変更して試したところ期待していた機能(一括保存)になりました! 長時間付き合っていただきありがとうございましたm(_ _)m
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問