🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Ruby on Rails

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

Q&A

解決済

1回答

823閲覧

チャットルーム作成時にsaveメソッドがrollbackされ、保存されない

lyon

総合スコア5

Ruby on Rails

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

0グッド

0クリップ

投稿2021/01/09 02:26

前提・実現したいこと

Ruby on Railsでチャットアプリのようなものを作っています。
現在は複数ユーザーを含めたチャットルームの生成機能を実装しようとしています。

発生している問題・エラーメッセージ

チャットルームを作成するために、

  • usersテーブル(devise使用)
  • roomsテーブル ※チャットルームのためのテーブル
  • room_usersテーブル(中間テーブル)

を準備し、いざチャットルームを作成しようとすると、Users is invalidとエラーメッセージが吐き出され、ルームの新規作成ができない状態です。

該当のソースコード

model

Ruby

1# Userモデル 2class User < ApplicationRecord 3 extend ActiveHash::Associations::ActiveRecordExtensions 4 # Include default devise modules. Others available are: 5 # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable 6 devise :database_authenticatable, :registerable, 7 :recoverable, :rememberable, :validatable 8 PASSWORD_REGEX = /\A(?=.*?[a-z])(?=.*?\d)[a-z\d]+\z/i.freeze 9 validates_format_of :password, with: PASSWORD_REGEX, message: 'include both letters and numbers' 10 with_options presence: true do 11 validates :nickname 12 validates :birthday 13 end 14 with_options numericality: { other_than: 1, message: 'must be chosen'} do 15 validates :study_genre_id 16 end 17 validates :email, uniqueness: { case_sensitive: false } 18 19 has_many :room_users 20 has_many :rooms, through: :room_users 21 belongs_to_active_hash :study_genre 22 23end 24

Ruby

1# roomモデル 2class Room < ApplicationRecord 3 has_many :room_users 4 has_many :users, through: :room_users 5 has_one_attached :image 6 validates :title, presence: true 7end

Ruby

1# room user モデル 2class RoomUser < ApplicationRecord 3 belongs_to :room 4 belongs_to :user 5end

####view

HTML

1<div class="registration-body"> 2 <%= form_with model: @room, url: rooms_path(@room), class: 'registration-main', local: true do |f| %> 3 <div class='form-wrap'> 4 <div class='form-header'> 5 <h1 class='form-header-text'> 6 学習報告ルームの情報を入力 7 </h1> 8 </div> 9 10 <%= render 'shared/error_messages', model: f.object %> 11 12 <div class="form-group"> 13 <div class='form-text-wrap'> 14 <label class="form-text">ルーム画像</label> 15 </div> 16 <div class="click-upload"> 17 <p> 18 クリックしてファイルをアップロード 19 </p> 20 <%= f.file_field :image, class:"room-image" %> 21 </div> 22 </div> 23 24 <div class="form-group"> 25 <div class='form-text-wrap'> 26 <%= f.label :title, "ルーム名", class:"form-text" %> 27 <span class="indispensable">必須</span> 28 </div> 29 <%= f.text_area :title, class:"input-default", placeholder:"例) rails勉強会", maxlength:"40" %> 30 </div> 31 <div class="form-group"> 32 <div class='form-text-wrap'> 33 <%= f.label :purpose_room, "この学習報告ルームの目的", class:"form-text" %> 34 <span class="indispensable">※140字まで</span> 35 </div> 36 <%= f.text_area :purpose_room, class:"input-introduction", placeholder:"ルームの説明(学習テーマ、目的、学習目標等)例) Ruby on Railsの学習報告ルームです。webエンジニアを目指して学習を行っていきましょう。平日2時間、休日8時間学習しましょう", maxlength:"140" %> 37 </div> 38 <div class="form-group"> 39 <div class='form-text-wrap'> 40 <label class="form-text">メンバーを選択してください</label> 41 <span class="indispensable">必須</span> 42 </div> 43 <select name="room[user_ids][]" > 44 <option value="">ユーザーを選択してください</option> 45 <% User.where.not(id: current_user.id).each do |user| %> 46 <option value=<%= user.id %>><%= user.nickname %></option> 47 <% end %> 48 </select> 49 <!-- これで現在ログインしているユーザーの情報も含められる --> 50 <input name="room[user_ids][]" type="hidden" value=<%= current_user.id %>> 51 </div> 52 53 54 <div class="form-group"> 55 <h2 class='form-bottom-text'> 56 「ルーム作成」ボタンを押すと、学習報告ルームが生成されます。 57 </h2> 58 </div> 59 <div class='register-btn'> 60 <%= f.submit "ルーム作成" ,class:"register-link" %> 61 </div> 62 </div> 63 <% end %> 64</div>

controller

Ruby

1# applicationコントローラー 2class ApplicationController < ActionController::Base 3 before_action :authenticate_user! 4 before_action :configure_permitted_parameters, if: :devise_controller? 5 6 private 7 8 def configure_permitted_parameters 9 devise_parameter_sanitizer.permit(:sign_up, keys: [:nickname, :birthday, :study_genre_id, :self_introduction]) 10 end 11end

Ruby

1# rooms コントローラー 2class RoomsController < ApplicationController 3 before_action :authenticate_user!, only:[:index] 4 5 def index 6 end 7 8 def new 9 @room = Room.new 10 end 11 12 def create 13 @room = Room.new(room_params) 14 if @room.save 15 redirect_to root_path 16 else 17 render :new 18 end 19 end 20 21 private 22 23 def room_params 24 params.require(:room).permit(:image, :title, :purpose_room, user_ids: []) 25 end 26 27end 28

試したこと

まず、ちゃんと@roomを作成するにあたりパラメーターが送られているか確認するため、コントローラーにbinding.pryを追記して検証しました。
すると

# ターミナル [1] pry(#<RoomsController>)> params => <ActionController::Parameters {"authenticity_token"=>"r1SvQ8cy9JigppFTk5G6mG/OYUM61AbFq0Rhl1kD2Sy42j5nSh6vNU0juvffGnJIPlwRd5vjzC47N4FhNmOMYA==", "room"=> <ActionController::Parameters {"image"=>#<ActionDispatch::Http::UploadedFile:0x00007fb29901ffb0 @tempfile=# <Tempfile:/var/folders/dt/2sln7kx14rl51gk_97gkhj2m0000gn/T/RackMultipart20210109-26266-qu1y9u.png>, @original_filename="arrow_top.png", @content_type="image/png", @headers="Content-Disposition: form-data; name=\"room[image]\"; filename=\"arrow_top.png\"\r\nContent-Type: image/png\r\n">, "title"=>"test", "purpose_room"=>"test", "user_ids"=>["2", "1"]} permitted: false>, "commit"=>"ルーム作成", "controller"=>"rooms", "action"=>"create"} permitted: false>

ということで、受け取れているようです。
そこで@room.saveを行うと、以下の通り、

# ターミナル [2] pry(#<RoomsController>)> @room.save (0.2ms) BEGIN ↳ (pry):2:in `create' User Exists? (0.3ms) SELECT 1 AS one FROM `users` WHERE `users`.`email` = 'tester@com' AND `users`.`id` != 2 LIMIT 1 ↳ (pry):2:in `create' User Exists? (0.3ms) SELECT 1 AS one FROM `users` WHERE `users`.`email` = 'test@test.com' AND `users`.`id` != 1 LIMIT 1 ↳ (pry):2:in `create' (0.2ms) ROLLBACK ↳ (pry):2:in `create' => false

と出てきており、saveがうまくいっていないようです。(rollbackされている)
そこで、過去質問されていた記事(https://teratail.com/questions/196445)を参考に、中間テーブルを

Ruby

1class RoomUser < ApplicationRecord 2 # optional: true を追記 3 belongs_to :room, optional: true 4 belongs_to :user, optional: true 5end 6

としましたが、結果は変わらず。

また、userモデルのemailのuniquenessが邪魔しているのでは?という記事も見かけた(リンクは見失いました、、)ので試してみると、

# ターミナル [1] pry(#<RoomsController>)> @room.save => false

幾分淡白に、、@room.save!を試すと、

# ターミナル [2] pry(#<RoomsController>)> @room.save! ActiveRecord::RecordInvalid: Validation failed: Users is invalid from /Users/yajimaryoga/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.4/lib/active_record/validations.rb:80:in `raise_validation_error'

結局、抜け出せない状態です、、

環境

  • Ruby: 2.6.5
  • Rails: 6.0.3.4
  • ユーザー登録: devise

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

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

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

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

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

guest

回答1

0

ベストアンサー

関連データをこういう形で作ったことは無いのですが。。。。
ここを参考にviewの修正からやってみてください。

viewをこのまま進めるとしたら room_params から user_ids を除いてsaveしたあと、
user_ids をloopで回して @room.users_create でつくる。

投稿2021/01/09 09:14

winterboum

総合スコア23567

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

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

lyon

2021/01/09 11:09

ご回答ありがとうございます。 今回はすでに登録済みのuserデータを、中間テーブルを通してroomテーブルと紐付けるものでしたが、頂いたリンクも非常に参考になる情報でした。 (質問の仕方をもう少し工夫します。) 他のやり方を調べると、roomモデルのusersアソシエーションの末尾に、validate: false を追記することで設定していないバリデーションを外すことができるらしく、その方法でエラーが解決しました。
winterboum

2021/01/09 12:44

多対多の場合、中間テーブルではなく、相手の多を指定すれば参考の方法で行けると思いますよ
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問