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

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

詳細はこちら
Ruby

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

Ruby on Rails 6

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

Q&A

解決済

3回答

911閲覧

undefined method `id'  idはどこへ行ってしまったのか

motoki6318

総合スコア2

Ruby

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

Ruby on Rails 6

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

0グッド

0クリップ

投稿2021/03/16 10:25

編集2021/03/17 09:04

アプリの仕様

  • キャンプの記録をするアプリです
  • ユーザーはキャンプのタイトルとキャンプスタイルという名のタグをつけて投稿します。
  • キャンプタイトルとタグを同時に保存するためにフォームオブジェクトを使用しています。
  • ユーザーは自分の持っているアイテムを登録できます。
  • キャンプには複数のアイテムを持っていける。アイテムは何度でもキャンプに持っていけることからキャンプとアイテムは多対多の関係です。
  • キャンプ記録を作成する際持って行ったアイテムの登録を実現させたい

エラー内容

イメージ説明

undefined method `id' for #<CampTags:0x00007ff6085ac980> Request Parameters: {"authenticity_token"=>"6h75fWC/+zhPx96ZoRyir+kyevpfqLqawP75tlOKrGHOXHlfKtMlG0qvpQMLgrjc5Ld3x1bvduvrtllnJPZagw==", "camp_tags"=>{"title"=>"bboobb", "style"=>"無骨キャンプ", "item_ids"=>["1", "2"]}, "commit"=>"Send"}
  • @camp.idを取り出したいがidがないと出る

状況

  • @item_idsをeachにかけることでitemのidを一つずつ取り出すのは成功しています。
  • campのデータについてはtagと同時に作成されるためFormオブジェクトで保存
  • itemに関しては保存してあったものをselectによって選択する
  • campとtagの情報については登録保存は成功している
  • @camp_id = 3と直球にidを指定してやるとitemとcampの関連付けはできた
  • @campを取り出せれば解決する
  • @campを指定すると:camp_tagsからidを取り出そうとするため@camp.idが無いとエラーになる

@campを確認するとidは保存されているようです。

>> @camp => #<CampTags:0x00007ff6085ac980 @errors=#<ActiveModel::Errors:0x00007ff6085ac250 @base=#<CampTags:0x00007ff6085ac980 ...>, @messages={}, @details={}>, @title="bboobb", @style="無骨キャンプ", @item_ids=["1", "2"], @user_id=1, @validation_context=nil, @camp=#<Camp id: 36, user_id: 1, title: "bboobb", created_at: "2021-03-17 06:28:53", updated_at: "2021-03-17 06:28:53">, @tag_id=#<Tag id: 4, style: "無骨キャンプ", created_at: "2021-03-16 09:04:46", updated_at: "2021-03-16 09:04:46">> >>

該当するコード

コントローラー

campscontroller

1class CampsController < ApplicationController 2 before_action :authenticate_user!, except: [:index, :show] 3 before_action :set_item, only: [:index, :new, :show, :create] 4 def index 5 @tags = Tag.all 6 end 7 8 def new 9 @camp = CampTags.new 10 end 11 12 def create 13 @camp = CampTags.new(camp_params) 14 15 if @camp.valid? 16 @tag_list = camp_params[:style].split(/[[:blank:]]+/).select(&:present?) 17 @camp.save(@tag_list) 18 @camp_id = @camp.id 19 @item_ids = @camp.item_ids 20 @item_ids.each do |item_id| 21 CampItemRelation.create(camp_id: @camp_id, item_id: item_id) 22 end 23 return redirect_to root_path 24 else 25 render "new" 26 end 27 end 28 29 private 30 31 def camp_params 32 params.require(:camp_tags).permit(:title, :style, item_ids: []).merge(user_id: current_user.id) 33 end 34 35 def set_item 36 if user_signed_in? 37 user = User.find(current_user.id) 38 @items = user.items 39 end 40 end 41 42end

formオブジェクト

camptagsrb

1class CampTags 2 3 include ActiveModel::Model 4 attr_accessor :title, :style, :user_id, :item_ids 5 6 with_options presence: true do 7 validates :title 8 validates :style 9 end 10 11 def save(tag_list) 12 @camp = Camp.create(user_id: user_id, title: title) 13 tag_list.each do |tag| 14 unless Tag.find_by(style: tag) 15 @tag = Tag.create(style: tag) 16 CampTagRelation.create(camp_id: @camp.id, tag_id: @tag.id) 17 else 18 @tag_id = Tag.find_by(style: tag) 19 CampTagRelation.create(camp_id: @camp.id, tag_id: @tag_id.id) 20 end 21 end 22 end 23 24end

入力フォーム

newhtmlerb

1<div class="wrapper"> 2 <div class="side-ber"> 3 <%= render "side_ber" %> 4 </div> 5 <%= form_with model: @camp, url: camps_path, class:'form-wrap', local: true do |f| %> 6 <div class='form'> 7 <div class="title-field"> 8 <%= f.label :title, "キャンプタイトル" %> 9 <%= f.text_area :title, class:"input-title" %> 10 </div> 11 <div class="tag-field", id='tag-field'> 12 <%= f.label :style, "キャンプスタイル" %> 13 <%= f.text_field :style, class:"input-tag" %> 14 </div> 15 16 <p>使用アイテムを選択 </p> 17 <select name="camp_tags[item_ids][]" multiple> 18 <% @items.each do |item| %> 19 <option value=<%= item.id %>><%= item.name %></option> 20 <% end %> 21 </select> 22 23 24 </div> 25 <div class="submit-post"> 26 <%= f.submit "Send", class: "submit-btn" %> 27 </div> 28 <% end %> 29</div>

ルーティング

Rails.application.routes.draw do root to: "camps#index" resources :camps, only: [:new, :create, :show] resources :items, only: [:new, :create, :show] devise_for :users end

テーブル同士のアソシエーション

itemmodel

1class Item < ApplicationRecord 2 extend ActiveHash::Associations::ActiveRecordExtensions 3 belongs_to :genre 4 belongs_to :user 5 has_many :camp_item_relations 6 has_many :camps, through: :camp_item_relations 7 8 9 with_options presence: true do 10 validates :genre_id, numericality: { other_than: 1, message: 'Select'} 11 validates :name 12 validates :feature 13 validates :price, numericality: { with: /\A[0-9]+\z/, message: 'Half-width number' } 14 end 15 16end

campmodel

1class Camp < ApplicationRecord 2 belongs_to :user 3 has_many :camp_tag_relations 4 has_many :tags, through: :camp_tag_relations 5 has_many :camp_item_relations 6 has_many :items, through: :camp_item_relations 7 8 validates :title, presence: true 9 10end

itemとcampの中間テーブル

campitemrelations

1class CampItemRelation < ApplicationRecord 2 belongs_to :camp 3 belongs_to :item 4end

試したこと

コントローラーではitem_idsを扱える
フォームオブジェクトではcamp.idを扱えることから
CampItemRelation.createでのcampとitemのidの保存を別々で行ってみた。
最初にフォームオブジェクトでCampItemRelation.create(camp_id: @camp.id)として
次にコントローラーでCampItemRelation.create(item_id: item_id)
こうすることでデータを扱えるうちに保存できると考えましたが、データはどうやら同時に保存しないといけないらしくこれらの記述は読み飛ばされたようでした。

controller

1class CampsController < ApplicationController 2 before_action :authenticate_user!, except: [:index, :show] 3 before_action :set_item, only: [:index, :new, :show, :create] 4 def index 5 @tags = Tag.all 6 end 7 8 def new 9 @camp = CampTags.new 10 end 11 12 def create 13 @camp = CampTags.new(camp_params) 14 15 if @camp.valid? 16 @tag_list = camp_params[:style].split(/[[:blank:]]+/).select(&:present?) 17 @camp.save(@tag_list) 18 19 @item_ids = @camp.item_ids 20 @item_ids.each do |item_id| 21 CampItemRelation.create(item_id: item_id) 22 end 23 return redirect_to root_path 24 else 25 render "new" 26 end 27 end 28 29 private 30 31 def camp_params 32 params.require(:camp_tags).permit(:title, :style, item_ids: []).merge(user_id: current_user.id) 33 end 34 35 def set_item 36 if user_signed_in? 37 user = User.find(current_user.id) 38 @items = user.items 39 end 40 end 41 42end

fromobject

1class CampTags 2 3 include ActiveModel::Model 4 attr_accessor :title, :style, :user_id, :item_ids 5 6 with_options presence: true do 7 validates :title 8 validates :style 9 end 10 11 def save(tag_list) 12 @camp = Camp.create(user_id: user_id, title: title) 13 CampItemRelation.create(camp_id: @camp.id) 14 tag_list.each do |tag| 15 unless Tag.find_by(style: tag) 16 @tag = Tag.create(style: tag) 17 CampTagRelation.create(camp_id: @camp.id, tag_id: @tag.id) 18 else 19 @tag_id = Tag.find_by(style: tag) 20 CampTagRelation.create(camp_id: @camp.id, tag_id: @tag_id.id) 21 end 22 end 23 end 24 25end

試したこと2

save処理後すぐに保存された最新のレコードを取り出す

def create @camp = CampTags.new(camp_params) if @camp.valid? @tag_list = camp_params[:style].split(/[[:blank:]]+/).select(&:present?) @camp.save(@tag_list) camp = Camp.order(updated_at: :desc).limit(1) @camp_id = camp.ids @item_ids = @camp.item_ids binding.pry @item_ids.each do |item_id| CampItemRelation.create(camp_id: @camp_id, item_id: item_id) end return redirect_to root_path else render "new" end end

camp = Camp.order(updated_at: :desc).limit(1)
とすることで最新のレコードを一個取り出しています。

イメージ説明

答えはおそらくもうすぐそこです!
どうかご教授ください!

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

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

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

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

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

guest

回答3

0

最善策とは言い難いですが、、、

camp = Camp.order(updated_at: :desc).limit(1)
をsaveメソッドの処理後すぐに記述をすることで最新のレコードを取り出す。
そのレコードのidを取り出すために@camp_id = camp.idsとする
このままだと@camp_idは配列に入ったままなのでCampItemRelation.create(camp_id: @camp_id[0], item_id: item_id)として配列の一番目を取得させることで解決!!!!

投稿2021/03/17 09:28

編集2021/03/17 09:36
motoki6318

総合スコア2

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

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

0

def save(tag_list) @camp = Camp.create(user_id: user_id, title: title) tag_list.each do |tag| ..... end end @camp end
と、最後に@campして@campを返したら良いのでは

投稿2021/03/16 13:52

winterboum

総合スコア23567

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

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

motoki6318

2021/03/16 23:14

回答ありがとうございます。 回答のとおりに返してみようとしましたがおなじ結果でした。 フォームオブジェクト内では@campを扱えるのですが、item.idを扱えない campsコントローラー内では、item_idsを扱えるが@campを扱えないという状況です。
winterboum

2021/03/16 23:21

?? 同じ というのは 全く同じ ですか? 同じような ですか? 回答の様にすると 戻りは Camp.save の結果なので 『CampTag』にidはない とはならないはず。 エラーが画像で載ってるとコピペできないので不便。テキストでできません?
maisumakun

2021/03/16 23:25

@camp.saveの結果を受け取っていないので、そこだけ書き換えても改善はされないですね…
winterboum

2021/03/16 23:27

def create @camp = CampTags.new(camp_params) では受け取ってません?
maisumakun

2021/03/16 23:29

途中に「@camp.save(@tag_list)」だけの行があって、saveの返り値は捨てられています。
winterboum

2021/03/16 23:41

そうなのか、、、
guest

0

ベストアンサー

@camp = CampTags.new(camp_params)となっていますが、CampTags#idは存在しません。

このメソッドを実装するか、あるいはどこかでフォームオブジェクトから純粋なモデルオブジェクトを取り出しておく必要があるかと思います。

投稿2021/03/16 11:30

maisumakun

総合スコア145965

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

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

motoki6318

2021/03/16 23:17

回答ありがとうございます。 それはフォームオブジェクトのsaveメソッド以外のメソッドを作ってその中で保存の処理をすると言う考えで間違い無いですか?
maisumakun

2021/03/16 23:25

> それはフォームオブジェクトのsaveメソッド以外のメソッドを作ってその中で保存の処理をすると言う考えで間違い無いですか? えっと、どうしてそう考えましたか?
motoki6318

2021/03/16 23:46

saveメソッド内で@campを保存後ならcamp.idが出来上がっているので camps_controller.rbでsave処理の後に@camp_id = @camp.idとしてidを変数に入れているつもりなのですがsaveメソッドの処理を呼び出す前に@camp.idを探そうとしている状態になっていることで生じているエラーだと私は考えています。
maisumakun

2021/03/16 23:49

> saveメソッド内で@campを保存後ならcamp.idが出来上がっているので 出来上がってはいるのですが、現状の方法では取得できない、という話です。
motoki6318

2021/03/17 00:13 編集

camps_controller.rbで@camp.save(@tag_list)より下の記述は@camp保存後に読まれているということですか? 保存後にidが取り出せないのは何故でしょうか 現状ではcamp_tagsからcamp.idを探そうとしているということでしょうか
maisumakun

2021/03/17 00:34

> 現状ではcamp_tagsからcamp.idを探そうとしているということでしょうか そのとおりです。
motoki6318

2021/03/17 01:50

> このメソッドを実装するか、あるいはどこかでフォームオブジェクトから純粋なモデルオブジェクトを取り出しておく必要があるかと思います。 これをもう少し詳しく教えてください。またはどのように検索すると良いでしょうか? 回答お待ちしております。
motoki6318

2021/03/17 08:55

少し無理やりですがsaveメソッド後に ```camp = Camp.order(updated_at: :desc).limit(1)``` ```@camp_id = camp.ids``` として、保存された最新のレコードを取り出すようにしてみました。 うまいこと最新のidを取り出せましたが、保存がうまくいってません何が原因でしょうか? ``` def create @camp = CampTags.new(camp_params) if @camp.valid? @tag_list = camp_params[:style].split(/[[:blank:]]+/).select(&:present?) @camp.save(@tag_list) camp = Camp.order(updated_at: :desc).limit(1) @camp_id = camp.ids @item_ids = @camp.item_ids @item_ids.each do |item_id| CampItemRelation.create(camp_id: @camp_id, item_id: item_id) end return redirect_to root_path else render "new" end end ``` ``` From: /Users/motokikeisuke/projects/camp-style/app/controllers/camps_controller.rb:21 CampsController#create: 12: def create 13: @camp = CampTags.new(camp_params) 14: 15: if @camp.valid? 16: @tag_list = camp_params[:style].split(/[[:blank:]]+/).select(&:present?) 17: @camp.save(@tag_list) 18: camp = Camp.order(updated_at: :desc).limit(1) 19: @camp_id = camp.ids 20: @item_ids = @camp.item_ids => 21: binding.pry 22: @item_ids.each do |item_id| 23: CampItemRelation.create(camp_id: @camp_id, item_id: item_id) 24: end 25: return redirect_to root_path 26: else [1] pry(#<CampsController>)> @camp_id => [4] [2] pry(#<CampsController>)> @item_ids => ["1", "2"] [3] pry(#<CampsController>)> ``` だいぶ答えに近づいたと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問