LoginSignup
2
1

More than 5 years have passed since last update.

Railsでアソシエーション機能の実装

Last updated at Posted at 2018-06-02

参考:https://qiita.com/Kohei_Kishimoto0214/items/cb9a3d3da57708fb52c9

VideoモデルとCategoryモデルを多対多の関係にする

Videoモデルは既に作成されているので既存のものがあるのでそれを使用

Categoryモデルの作成

rails g model Category name:string

作成されたマイグレーションファイルを編集

migrate/XXXXX.rb
  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を持っているという風にしたい

models/video.rb
  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を持っているとしたい。

models/category.rb
【下記項目追加】

  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に関しては作成時にアソシエーションを設定しているので、デフォルトのまま使用

models/vodeo_category.rb
  belongs_to :video
  belongs_to :category
end

videoに配列でcategory_idを渡せるようにする

videos_controller.rb
 #strongparams
  private
    def video_params
        params.require(:video).permit(:artist_name, :youtube_id, :music_name, { :category_id => [] }) ⇦追加
    end

{:category_id => []} の部分を追加

ついでにcreateアクションも編集

videos_controller.rb
  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の順番を入れ替えないといけないみたい。

models/video.rb
  has_many :video_categories  ⇦順番入れ替え
  has_many :categories, through: :video_categories ⇦順番入れ替え
models/category.rb
  has_many :video_categories ⇦順番入れ替え
  has_many :videos, through: :video_categories ⇦順番入れ替え

これで再度コンソールを動かすと問題なく値が返ってくるのでOK!

アソシエーションが問題なく機能していることを確認!完了!

フォームから保存できない

実際にフォームから保存すると保存できなかったので、ViewとControllerを編集
※コメントアウトしてる部分を削除

videos_controller.rb
  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
videos/views.html.erb
<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

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1