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

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

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

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

SQL

SQL(Structured Query Language)は、リレーショナルデータベース管理システム (RDBMS)のデータベース言語です。大きく分けて、データ定義言語(DDL)、データ操作言語(DML)、データ制御言語(DCL)の3つで構成されており、プログラム上でSQL文を生成して、RDBMSに命令を出し、RDBに必要なデータを格納できます。また、格納したデータを引き出すことも可能です。

Ruby on Rails

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

Q&A

解決済

2回答

3757閲覧

saveメソッドによるデータ保存ができない

退会済みユーザー

退会済みユーザー

総合スコア0

Ruby

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

SQL

SQL(Structured Query Language)は、リレーショナルデータベース管理システム (RDBMS)のデータベース言語です。大きく分けて、データ定義言語(DDL)、データ操作言語(DML)、データ制御言語(DCL)の3つで構成されており、プログラム上でSQL文を生成して、RDBMSに命令を出し、RDBに必要なデータを格納できます。また、格納したデータを引き出すことも可能です。

Ruby on Rails

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

0グッド

0クリップ

投稿2020/09/03 07:22

編集2020/09/04 06:15

前提・実現したいこと

概要
Ruby(Ruby on Rails)で自身と相手で連絡が可能なアプリを作成しています。
(自身と任意の別ユーザーのプロジェクト(連絡板)を作成し、その中で連絡をとることができる機能)
その中で実現したいことは、
プロジェクトを新規作成した際に、中間テーブルに外部キーであるuser_id、project_idを併せて保存する
ということですがこの点で先へ進めない状態となっています。

設定背景
モデル    :user、project、pro_user(左記テーブルの中間テーブル)
導入したgem:devise(ユーザー管理)、pry-rails

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

発生している問題

  • binding.pryでプロセスを止めフォームから送られたparamsの内容を確認。projectsテーブル及びpro_userテーブルへの保存に必要なデータ(プロジェクト名及びuser_id)は含まれていることをターミナル上で確認。
  • 上記を確認した後、saveメソッドで保存の可否をターミナル上で確認したところ、以下のターミナルの表示通りでROLLBACKがされ保存できない状況となった。

エラーメッセージ

6: def create 7: @project = Project.new(project_params) => 8: binding.pry 9: if @project.save 10: redirect_to root_path 11: else 12: render :new 13: end 14: end [1] pry(#<ProjectsController>)> @project.save (0.4ms) BEGIN ↳ (pry):1:in `create' User Exists? (0.4ms) SELECT 1 AS one FROM `users` WHERE `users`.`email` = BINARY 't@t' AND `users`.`id` != 1 LIMIT 1 ↳ (pry):1:in `create' User Exists? (0.6ms) SELECT 1 AS one FROM `users` WHERE `users`.`email` = BINARY 's@s' AND `users`.`id` != 2 LIMIT 1 ↳ (pry):1:in `create' (0.2ms) ROLLBACK ↳ (pry):1:in `create' => false

お伺いしたいこと
原因と考えられる点やアクションプランがあればご助言いただきたく存じます。

  • 以下に記述している試したこと(ターミナル上での原因探索等)を踏まえ、ビューファイルの記述不備が根本原因ではなく、モデルやデータベースに何らかの不備があると推測しています
  • しかし、知識不足ゆえに確実な根本原因と言い切れないこと、また対策のアクションプランが手詰まりであるため、今回質問をさせていただきました。

該当のソースコード

routes.rb

Ruby

1Rails.application.routes.draw do 2 devise_for :users 3 root to: 'messages#index' 4 resources :projects, only: [:new, :create] 5end

projects_controller.rb

Ruby

1class ProjectsController < ApplicationController 2 def new 3 @project = Project.new 4 end 5 6 def create 7 @project = Project.new(project_params) 8 if @project.save 9 redirect_to root_path 10 else 11 render :new 12 end 13 end 14 15 private 16 17 def project_params 18 params.require(:project).permit(:name, user_ids:[]) 19 end 20end

models > user.rb

Ruby

1class User < ApplicationRecord 2 #アソシエーションを抜粋 3 has_many :pro_users 4 has_many :projects, through: :pro_users 5end

models > project.rb

Ruby

1class Project < ApplicationRecord 2 #アソシエーションを抜粋 3 has_many :pro_users 4 has_many :users, through: :pro_users, dependent: :destroy 5end

models > pro_user.rb

Ruby

1class ProUser < ApplicationRecord 2 belongs_to :user 3 belongs_to :project 4end

試したこと

  • ターミナルでのエラー原因探索(★情報追加依頼の方にお示しした箇所)

[1][4][5]で示されている通り、user_idをもとにusersテーブルからデータの取得ができていないことが原因と考えられる

[2] pry(#<ProjectsController>)> params => <ActionController::Parameters {"authenticity_token"=>"gE3lWZX40UGHzTvg/BkYV2WqROr6GK/QpV1PE6XHuqJUpauKuzxCY35jNKBKDOGVZJdKJVNlMu7KdRLttHWmkA==", "project"=><ActionController::Parameters {"name"=>"お試し", "user_ids"=>["1", "2"]} permitted: false>, "commit"=>"Create Project", "controller"=>"projects", "action"=>"create"} permitted: false> [3] pry(#<ProjectsController>)> project_params => <ActionController::Parameters {"name"=>"お試し", "user_ids"=>["1", "2"]} permitted: true> [4] pry(#<ProjectsController>)> @project.errors => #<ActiveModel::Errors:0x00007fed1b9ac328 @base=#<Project:0x00007fed1431d5b8 id: nil, name: "お試し", created_at: nil, updated_at: nil>, @details={:users=>[{:error=>:invalid}, {:error=>:invalid}]}, @messages={:users=>["is invalid"]}> [5] pry(#<ProjectsController>)> @project.errors.full_messages => ["Users is invalid"]
  • rails db:migrateの不備を疑う(カラムの型や外部キー設定に不備)

改めてrails db:migrate:resetを実行→以下の通り設定ができているものの結果は変わらず

+------------+-------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +------------+-------------+------+-----+---------+----------------+ | id | bigint(20) | NO | PRI | NULL | auto_increment | | user_id | bigint(20) | YES | MUL | NULL | | | project_id | bigint(20) | YES | MUL | NULL | | | created_at | datetime(6) | NO | | NULL | | | updated_at | datetime(6) | NO | | NULL | | +------------+-------------+------+-----+---------+----------------+
  • user_idsという配列のパラメータが複数あることが原因と仮定

ビューファイルをい1行コメントアウトすることで、現在のユーザーidがパラメータに含まれないようにしてから実行
→試行前と同じエラーが出現したため配列内のパラメータ数は根本原因ではない可能性が高いことが推測された

ERB

1<div class='project-form__field--right'> 2 <select name='project[user_ids][]'> 3  <option value=''>アサインメンバーを選択してください</option> 4  <% User.all.where.not(id: current_user.id). each do |user| %> 5   <option value=<%=user.id%>><%= user.last_name %><%= user.first_name %></option> 6  <% end %> 7 </select> 8 #以下の1行をコメントアウト 9 <input name='project[user_ids][]' type='hidden' value=<%=current_user.id>> 10</div>

####試したことの追記(2020/9/4)

  • if @prject.save!(@prject.errors)により、User is invalid がエラー内容として出現していたことから、Validationが邪魔をしているため保存ができていないことを疑った

→UserモデルのValidationを一時的にコメントアウトした結果、意図したデータ保存がされた(プロジェクトと同時に中間テーブルのデータも保存)
→根本原因はValidationであることが判明した。しかし、ValidationはUser登録の時に必須でありコンフリクトが生じてしまった。

Ruby

1class User < ApplicationRecord 2 # Include default devise modules. Others available are: 3 # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable 4 devise :database_authenticatable, :registerable, 5 :recoverable, :rememberable, :validatable 6 7 # japanese_letters = /\A[ぁ-んァ-ン一-龥]+\z/ 8 # with_options presence: true do 9 # validates :last_name, format: { with: japanese_letters, message: 'is not full-width characters' } 10 # validates :first_name, format: { with: japanese_letters, message: 'is not full-width characters' } 11 # validates :email, uniqueness: { case_sensitive: true }, 12 # format: { with: /\w+([-+.]\w+)*@\w+([-.]\w+)*/ } 13 # validates :password, length: {minimum: 6 }, 14 # format: { with: /\A(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]+\z/, message: 'includes both half-width letters and half-width numbers' } 15 # validates :company, :password_confirmation 16 # end 17 18 has_many :pro_users 19 has_many :projects, through: :pro_users 20 has_many :messages 21end

ターミナル

Parameters: {"authenticity_token"=>"ZOq+q72nyTZK7+6DAc1vIWclC/OS2UauGGAJ+BjjsUWwAvB4k2NaFLNB4cO32JbjZhgFPDuk25B3SFQGCVGtdw==", "project"=>{"name"=>"お試し", "user_ids"=>["1", "2"]}, "commit"=>"Create Project"} User Load (0.3ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 2 ORDER BY `users`.`id` ASC LIMIT 1 User Load (0.4ms) SELECT `users`.* FROM `users` WHERE `users`.`id` IN (1, 2) ↳ app/controllers/projects_controller.rb:7:in `create' (0.2ms) BEGIN ↳ app/controllers/projects_controller.rb:8:in `create' Project Create (0.9ms) INSERT INTO `projects` (`name`, `created_at`, `updated_at`) VALUES ('お試し', '2020-09-04 05:43:52.654047', '2020-09-04 05:43:52.654047') ↳ app/controllers/projects_controller.rb:8:in `create' ProUser Create (0.2ms) INSERT INTO `pro_users` (`user_id`, `project_id`, `created_at`, `updated_at`) VALUES (1, 4, '2020-09-04 05:43:52.657678', '2020-09-04 05:43:52.657678') ↳ app/controllers/projects_controller.rb:8:in `create' ProUser Create (0.2ms) INSERT INTO `pro_users` (`user_id`, `project_id`, `created_at`, `updated_at`) VALUES (2, 4, '2020-09-04 05:43:52.659268', '2020-09-04 05:43:52.659268') ↳ app/controllers/projects_controller.rb:8:in `create' (0.6ms) COMMIT

補足情報(FW/ツールのバージョンなど)

  • ruby '2.6.5'
  • rails (6.0.3.2)

以上、よろしくお願いいたします。

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

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

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

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

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

maisumakun

2020/09/03 07:33

ロールバックした直後に@project.errorsとして何かしらの情報は得られませんでしょうか?
退会済みユーザー

退会済みユーザー

2020/09/03 07:47

早々にご返信ありがとうございます。 binding.pryでプロセスを止めた後の操作という解釈でよろしいでしょうか。 相違がないようでしたら@project.errorsで得られた情報は、上記試したことの結果と重複いたしますが下記の通りでした。 => #<ActiveModel::Errors:0x00007fed1b9ac328 @base=#<Project:0x00007fed1431d5b8 id: nil, name: "お試し", created_at: nil, updated_at: nil>, @details={:users=>[{:error=>:invalid}, {:error=>:invalid}]}, @messages={:users=>["is invalid"]}> この記述からuserデータが取得できていないのではと疑いましたが、次のアクションプランが思い浮かばない次第です。
no1knows

2020/09/03 12:43

createアクションで、user_idsという配列のパラメータをそれぞれのusr_idに分けてからsaveする必要があるのではないでしょうか?
退会済みユーザー

退会済みユーザー

2020/09/03 13:32

ご助言ありがとうございます。 >user_idsという配列のパラメータをそれぞれのusr_idに分けてからsave 「配列に複数のuser_idが含まれていることが原因の可能性がある」ということでしょうか。 「分けてからsave」の適切な記述がわかりませんでしたので、まずはビューファイルを修正しパラメータに含まれるuser_idが一つとなるように修正し、早速試してみました。(本文の試したことに詳細を追記) 結果としては、状況は変わらず@project.errorsにて以下のエラーが出現しております。 (ご助言の意図から外れた検証内容でしたら申し訳ございません。。) @details={:users=>[{:error=>:invalid} @messages={:users=>["is invalid"]}
guest

回答2

0

if @project.save一時的に  if @project.save! にするとsaveできない原因が表示されるので、それがあるとはっきりしますが、今ある情報からは
project には user_id, user_ids というattribute がないのにそれを設定しようとしている、というのが原因でしょう。

<select name='project[user_ids][]'
<select name='project[pro_users_attributes][user_ids][]'
とし、
params.require(:project).permit(:name, user_ids:[])
params.require(:project).permit(:name, pro_users_attributes: {user_ids:[]})
にし、
class Project に
accepts_nested_attributes_for :pro_users を追加

すればできるかな。。。

投稿2020/09/03 14:51

winterboum

総合スコア23567

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

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

退会済みユーザー

退会済みユーザー

2020/09/03 15:49

ご回答ありがとうございます。 関連を持ったモデルをまとめて操作するために、accepts_nested_attributes_forを設定してpro_usersテーブルにuser_idを保存する方針ですね。 上記の通り修正し試行したところ、今まで登録されなかったprojectテーブルはデータが保存され、一方で中間テーブルであるpro_usersテーブルには保存がされなかったという状況です。 原因(エラー)をターミナルにて確認したところ「Unpermitted parameter: :pro_users_attributes」と出現しており、user_id自体が渡されていないことが原因と考えられるかと思われました。 今回の件で、user_idの渡され方又は渡された後の処理が対処すべき箇所だと見当をつけることができました。
winterboum

2020/09/03 22:48

Unpermitted parameter: がでましたか。。。おかしいな。 (私の書いたのも含め)typoがないかは一度確認しておいてください。
退会済みユーザー

退会済みユーザー

2020/09/04 00:55

ご返信ありがとうございます。 winterboum様の意図する検証が正しく実施できるように、まずは誤記の見直しをさせていただきます!
guest

0

ベストアンサー

以下検証より解決できたため、当該質問をcloseさせていただきます。
今回コメント及び回答をいただいた皆さま、ご助言いただき有難うございました。

  • 仮説

以下、UserモデルのValidationを削除(コメントアウト)することでプロジェクトテーブル及び中間テーブルのデータが保存された。
中間テーブルとUsersテーブルはbelongs_to/has_manyの関係であることから、中間テーブルのデータ保存時にUserモデルのvalidationが作用し、saveメソッドが中止されていたことが推察された。

  • 検証・修正内容

validationを削除することはUser新規登録に差し支えるため不可。
そこで、validationのかかるタイミングをonオプションでUserの新規登録時のみに指定
以下の通り修正することで、User新規登録にも影響なく意図したデータ登録ができるようになった。

###修正前
Userモデル

Ruby

1class User < ApplicationRecord 2#該当箇所抜粋 3 japanese_letters = /\A[ぁ-んァ-ン一-龥]+\z/ 4 with_options presence: true, on: :registration do |registration| 5 registration.validates :last_name, format: { with: japanese_letters, message: 'is not full-width characters' } 6 registration.validates :first_name, format: { with: japanese_letters, message: 'is not full-width characters' } 7 registration.validates :email, uniqueness: { case_sensitive: true }, 8 format: { with: /\w+([-+.]\w+)*@\w+([-.]\w+)*/ } 9 registration.validates :password, length: {minimum: 6 }, 10 format: { with: /\A(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]+\z/, message: 'includes both half-width letters and half-width numbers' } 11 registration.validates :company, :password_confirmation 12 end 13 14 has_many :pro_users 15 has_many :projects, through: :pro_users 16 has_many :messages 17end

###修正後
Userモデル

Ruby

1class User < ApplicationRecord 2#該当箇所抜粋 3 japanese_letters = /\A[ぁ-んァ-ン一-龥]+\z/ 4 with_options presence: true, on: :registration do |registration| 5 registration.validates :last_name, format: { with: japanese_letters, message: 'is not full-width characters' } 6 registration.validates :first_name, format: { with: japanese_letters, message: 'is not full-width characters' } 7 registration.validates :email, uniqueness: { case_sensitive: true }, 8 format: { with: /\w+([-+.]\w+)*@\w+([-.]\w+)*/ } 9 registration.validates :password, length: {minimum: 6 }, 10 format: { with: /\A(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]+\z/, 11 message: 'includes both half-width letters and half-width numbers' } 12 registration.validates :company, :password_confirmation 13 end

新たに生成しカスタマイズしたdeviseのcontroller

Ruby

1class Users::RegistrationsController < Devise::RegistrationsController 2#変更箇所付近を抜粋 3def create 4 build_resource(sign_up_params) 5 6 resource.save(context: :registration) ←save以降を追記 7 yield resource if block_given? 8 if resource.persisted? 9(後略) 10end

投稿2020/09/04 12:10

編集2020/09/04 12:31
退会済みユーザー

退会済みユーザー

総合スコア0

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問