参考:https://qiita.com/Kohei_Kishimoto0214/items/cb9a3d3da57708fb52c9
VideoモデルとCategoryモデルを多対多の関係にする
Videoモデルは既に作成されているので既存のものがあるのでそれを使用
Categoryモデルの作成
rails g model Category name:string
作成されたマイグレーションファイルを編集
def change
create_table :categories do |t|
t.string :name, default: "", null: false
t.timestamps
end
end
end
マイグレーション実行
rails db:migrate
中間テーブルの作成
VideoCategoryモデル(Video_Categoriesテーブル)作成
rails g model VideoCategory Video:references Category:references
マイグレーションファイル編集
def change
create_table :video_categories do |t|
t.references :video, index: true, foreign_key: true
t.references :category, index: true, foreign_key: true
t.timestamps
end
end
indexについて
indexは、データベースにインデックスを付ける、という意味。
たとえば、この列で検索する、あるいはこの列を基準に並べ替える、という場合に、インデックスがあれば、全部のデータを調べずとも高速に行えます。ただし、インデックス自体もデータ容量や更新の際の処理が負荷となりますので、無闇矢鱈に付けても意味は無い。
foreign_keyについて
foreign_keyは、データベースに外部キー制約をかけるもの。これがあると、「データベースレベルで」video_idにはvideos.idにある値しか入れられなくなります。has_manyやbelongs_toなどを考える上で、「関連付けできないような値は絶対に来ない」という意味で、より安全なシステムを組める。
マイグレーション実行
rails db:migrate
モデル同士のアソシエーション設定
・videoモデルを編集
videoは複数のvideo_categoryを持っていて、
video_categoryを通して、複数のcategoryを持っているという風にしたい
has_many :categories, through: :video_categories ⇦追加
has_many :video_categories ⇦追加
validates :youtube_id, presence: true
validates :artist_name, presence: true
validates :music_name, presence: true
end
中間テーブルを通して繋がっているものには
through: :group_usersというkeyをつけます。
・categoryモデルを編集
こちらも同じように
categoryは複数のvideo_categoryを持っていて
video_categoryを通して、複数のvideoを持っているとしたい。
【下記項目追加】
has_many :videos, through: :video_categories
has_many :video_categories
accepts_nested_attributes_for :video_categories
accepts_nested_attributes_forについて
accepts_nested_attributes_for というkeyは、
他のモデルを一括で更新、保存できるようにするもの。
ここではcategoryを保存するのと同時にvideo_categoriesを更新できるようにしています。
・video_category
video_categoryに関しては作成時にアソシエーションを設定しているので、デフォルトのまま使用
belongs_to :video
belongs_to :category
end
videoに配列でcategory_idを渡せるようにする
#strongparams
private
def video_params
params.require(:video).permit(:artist_name, :youtube_id, :music_name, { :category_id => [] }) ⇦追加
end
{:category_id => []} の部分を追加
ついでにcreateアクションも編集
def create
@video = Video.new(video_params)
if @video.save
@category = Category.find_by(:name params[:category][:name]) ⇦追加
@video_cate = VideoCategory.new(video_id: @video.id, category_id: @category.id) ⇦追加
if @video_cate.save ⇦追加
redirect_to @video
else
render 'new'
end
end
end
アソシエーションが機能しているか確認
Railsコンソールで確認
v = Video.first
v.categories << Category.first
v.categories
v.video_categories
上記順番に確認していくが、エラーが出るので上手く機能していない様子。。。
ActiveRecord::HasManyThroughOrderError: Cannot have a has_many :through association 'Video#categories' which goes through 'Video#video_categories' before the through association is defined.
from /Users/tky/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/activerecord-5.1.6/lib/active_record/reflection.rb:975:in `check_validity!'
どうやらモデルのhas_manyの順番を入れ替えないといけないみたい。
has_many :video_categories ⇦順番入れ替え
has_many :categories, through: :video_categories ⇦順番入れ替え
has_many :video_categories ⇦順番入れ替え
has_many :videos, through: :video_categories ⇦順番入れ替え
これで再度コンソールを動かすと問題なく値が返ってくるのでOK!
アソシエーションが問題なく機能していることを確認!完了!
フォームから保存できない
実際にフォームから保存すると保存できなかったので、ViewとControllerを編集
※コメントアウトしてる部分を削除
def create
@video = Video.new(video_params)
if @video.save
# @category = Category.find_by(name: params[:category][:name])#categoryのnameを取得
# @video_cate = VideoCategory.new(video_id: @video.id, category_id: @category.id)#video_idとcategory_id取得
# if @video_cate.save
redirect_to @video
else
render 'new'
end
end
<div class="center jumbotron">
<% provide(:title, 'add music') %>
<h1>Add Music</h1>
</div>
<div class="nav">
<div class="col-md-6 col-md-offset-3">
<%= form_for @video do |f| %>
<%= render 'shared/error_messages' %>
<%= f.label :youtube_id %>
<%= f.text_field :youtube_id, placeholder: 'YoutubeId', class: "form-control"%>
<%= f.label :artist_name %>
<%= f.text_field :artist_name, class: "form-control"%>
<%= f.label :music_name %>
<%= f.text_field :music_name, class: "form-control"%>
<h2>Category選択</br>
Category選択のチェックボックス実装</h2>
<%= field_set_tag 'カテゴリー選択' do %>
<%= f.collection_check_boxes(:category_ids, Category.all, :id, :name) do |b| %>
<% b.label { b.check_box + b.text } %>
<% end %>
<% end %>
<div class="actions">
<%= f.submit "登録" %>
</div>
<% end %>
</div>
</div>
これでフォームからの保存も可能になった!
Controllerに余計な記述が多かったみたいですね。。。
ポリモフィックリレーション
あと、アソシエーションを実装する際に、ポリモフィックリレーションなる便利な機能があるみたいだが、現状良く分からないので。
こんな機能があるんだなということで。。。
http://ruby-rails.hatenadiary.com/entry/20141207/1417926599