解決したいこと:1対他モデルの一括登録機能
Railsで一括登録する機能を開発しています。
モデルを一括登録する手順を参考にしてやってみましたが
エラーでロールバックされてしまい登録することができません
バリデーションあたりに問題があると見て
コントローラーのバリデーション周りなど色々変えて試してみましたが
何時間立ってもできないため質問することにしました・・・
足りない情報があれば指摘していただけると助かります
問題が発生するまでの流れ・手順
new.html.erbでフォームに入力して登録ボタンを押した時
New!
エラー画面
app/views/tasks/new.html.erb
<%= form_with model: @tasks, url: tasks_path, local: true do |form| %> <% @tasks.collection.each do |task| %> <%= fields_for 'tasks[]', task, local: true do |field| %> <h2> <p><%= field.label :"title", class: 'label' %></p><br> <p><%= field.text_field :title, class: 'tasks-form-field'%></p><br> <% if @tasks.errors.include?(:title) %> <div class="alert alert-danger"> <%= @tasks.errors.messages[:title][0] %> </div> <% end %> <p><%= field.label :"priority", class: 'label' %></p><br> <p><%= field.select :priority, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], {include_blank: '選択してください'}, {class: 'tasks-form-box', required: true} %></p><br> <% if @tasks.errors.include?(:priority) %> <div class="alert alert-danger"> <%= @tasks.errors.messages[:priority][0] %> </div> <% end %> <p><%= field.label :"content", class: 'label' %></p><br> <p><%= field.text_area :content, class: 'tasks-form-area' %></p><br> <% if @tasks.errors.include?(:content) %> <div class="alert alert-danger"> <%= @tasks.errors.messages[:content][0] %> </div> <% end %> <% end %> <% end %> <%= form.submit '一括登録' %> <%= link_to '戻る', task_path(current_user) %> </h2> </div> <% 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 :tasks, dependent: :destroy end
app/models/task.rb
# == Schema Information # # Table name: tasks # # id :bigint not null, primary key # content :text(65535) # done :boolean # priority :integer # title :string(255) # created_at :datetime not null # updated_at :datetime not null # user_id :bigint # # Indexes # # index_tasks_on_user_id (user_id) # index_tasks_on_user_id_and_created_at (user_id,created_at) # # Foreign Keys # # fk_rails_... (user_id => users.id) # class Task < ApplicationRecord belongs_to :user default_scope -> { order(priority: :asc) } validates :priority, numericality: {message: '数値を入力してください!'}, uniqueness: {message: 'すでに設定したリストと優先順位がかぶっています'} validates :title, presence: {message: 'タイトルを入力してください'}, length: {minimum: 2, message: '2文字以上で入力してください'} validates :content, length: {maximum: 50, message: '登録できるのは50文字までです'} end
app/models/tasks_collection.rb
# Taskのコレクションモデル class TaskCollection include ActiveModel::Conversion extend ActiveModel::Naming extend ActiveModel::Translation include ActiveModel::AttributeMethods include ActiveModel::Validations attr_accessor :collection TASK_NUM = 3 # 同時にtaskを作成する数 # 初期化メソッド def initialize(current_user, attributes = []) if attributes.present? self.collection = attributes.map do |value| Task.new( user_id: current_user.id, content: value["content"], title: value["title"], priority: value["priority"] ) end else self.collection = TASK_NUM.times.map { Task.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/tasks_controller.rb
class TasksController < ApplicationController def new @tasks = TaskCollection.new(current_user) end #変更 @tasks = TaskCollection.new(current_user, tasks_params) if @tasks.save flash[:success] = "作成しました!" redirect_to task_path(current_user) else def update if current_user.update(user_tasks_params) flash[:success] = "編集しました!" redirect_to task_path(current_user) else render 'tasks/edit' end end private # ストロングパラメーターcreate用 def tasks_params params.require(:tasks) end # ストロングパラメーターupdate用 def user_tasks_params params.require(:user).permit(tasks_attributes: [:title, :content, :id, :priority, :admin]) 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 "/tasks" for 172.18.0.1 at 2021-03-12 08:00:41 +0000 web_1 | Processing by TasksController#create as HTML web_1 | Parameters: {"utf8"=>"✓", "authenticity_token"=>"YGJn1DCr5ZPyJoB9kLvySBof7vj2II4iLb6KQFj+OtXqy0C1bMZ162jmby9s+XZrTcLaHNIz2B4RK39fS7U9Lg==", "tasks"=>[{"title"=>"task0", "priority"=>"0", "content"=>""}, {"title"=>"task1", "priority"=>"1", "content"=>""}, {"title"=>"task2", "priority"=>"2", "content"=>""}], "commit"=>"一括登録"} web_1 | User Load (0.6ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 4 LIMIT 1 web_1 | (0.6ms) BEGIN web_1 | User Load (1.6ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 4 LIMIT 1 web_1 | Task Exists (0.8ms) SELECT 1 AS one FROM `tasks` WHERE `tasks`.`priority` = 0 LIMIT 1 web_1 | CACHE User Load (0.0ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 4 LIMIT 1 web_1 | Task Exists (0.8ms) SELECT 1 AS one FROM `tasks` WHERE `tasks`.`priority` = 1 LIMIT 1 web_1 | CACHE User Load (0.0ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 4 LIMIT 1 web_1 | Task Exists (3.7ms) SELECT 1 AS one FROM `tasks` WHERE `tasks`.`priority` = 2 LIMIT 1 web_1 | (0.7ms) ROLLBACK web_1 | "エラー" web_1 | Rendering tasks/new.html.erb within layouts/application web_1 | Rendered tasks/new.html.erb within layouts/application (14.4ms) web_1 | Rendered layouts/_shim.html.erb (1.1ms) web_1 | Rendered layouts/_head.html.erb (1440.8ms) web_1 | Rendered layouts/_header.html.erb (3.3ms) web_1 | Rendered layouts/_flash_messages.html.erb (1.3ms) web_1 | Rendered layouts/_footer.html.erb (7.9ms) web_1 | Completed 200 OK in 2111ms (Views: 1884.7ms | ActiveRecord: 13.0ms)
環境
rails: 5.2.3
ruby: 2.5.7
回答1件
あなたの回答
tips
プレビュー