アプリの仕様
- キャンプの記録をするアプリです
- ユーザーはキャンプのタイトルとキャンプスタイルという名のタグをつけて投稿します。
- キャンプタイトルとタグを同時に保存するためにフォームオブジェクトを使用しています。
- ユーザーは自分の持っているアイテムを登録できます。
- キャンプには複数のアイテムを持っていける。アイテムは何度でもキャンプに持っていけることからキャンプとアイテムは多対多の関係です。
- キャンプ記録を作成する際持って行ったアイテムの登録を実現させたい
エラー内容
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)
とすることで最新のレコードを一個取り出しています。
答えはおそらくもうすぐそこです!
どうかご教授ください!
回答3件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。