おすしたべたい

おすしがたくさん食べられるようにがんばるブログ

ActiveRecordでidがないテーブルにデータを保存しようとすると"nil is not a symbol"とか言われる

すんげハマったのでめも。

背景

create_join_tableでつくった中間テーブルのデータを更新したくて、中間テーブル用のモデルからsave!とかupdate!とかしようとしたわけです。

たとえば、 下記の用にOsushiクラスとNetaクラス、そして中間テーブル用のNetaOsushiクラスがあるとします。

class Osushi < ActiveRecord::Base
  has_many :neta_osushi
end

class Neta < ActiveRecord::Base
  has_many :neta_osushi
end

class NetaOsushi < ActiveRecord::Base
  belongs_to :osushi
  belongs_to :neta
end

このNetaOsushiクラスのmigrationクラスは以下のようになるでしょう。

class CreateJoinTableNetaOsushi < ActiveRecord::Migration
  def change
    create_join_table :neta, :osushi do |t|
      t.index :neta_id
      t.index :osushi_id
      t.timestamps
    end
  end
end

で、大人の事情でこのクラスのデータを更新しないといけなくなった場合に、

neta_osushi = NetaOsushi.find_by(neta_id: neta_id, osushi_id: osushi_id)
#
# 某かの代入などの処理
#
neta_osushi.save!

とかすると、nil is not a symbol とかすごい剣幕で怒られてしまい

ヒヤシンス!!!

nil is not a symbol…!!!」

とみおちゃんばりの剣幕で困り果ててしまいました。

解決方法

さてこれかなりドンピシャなQiitaの記事を発見したので御覧ください。

qiita.com

まさにこの状況ですなー。 ということで、複合主キーを設定しましょう。

まずは、複合主キーを扱うために、composite_primary_keysというgemを入れましょう。

github.com

Gemfileに下記一行追加してbundle installします。

gem 'composite_primary_keys', '~> 8.0.0'

ちなみにお使いのActiveRecordのバージョンによってインストールするバージョンも違うので、詳しくはcomposite_primary_keysのリポジトリのREADMEを御覧ください。
ちなみに8.xはActiveRecord4.2.x用です。

そんで、neta_osushiテーブルにunique indexをはります。

class AddIdnexToNetaOsushi < ActiveRecord::Migration
  def change    
    add_index :neta_osushi, [:neta_id, :osushi_id], unique: true, name: 'composite_index'
  end
end

最後に、モデル側で、こいつらがprimary keyだよ〜と教えてあげます。ここは、primary_keyではなく、primary_keys複数なので注意。

class NetaOsushi < ActiveRecord::Base

  self.primary_keys = :neta_id, :osushi_id

  belongs_to :osushi
  belongs_to :neta

end

これで往年の悩みは解決されました。

ゆるしてヒヤシンス!!! ゆるしてヒヤシンス