[Rails] belongs_to で参照名を変更した場合に生じる「rollback transaction」について

# 前提

以下の関連付けがあるとする。

```ruby:param_types.rb
class ParamType < ApplicationRecord
  has_many :param_values
end
```

```ruby:param_values.rb
class ParamValue < ApplicationRecord
  belongs_to :param_type, dependent: :destroy
end
```

`param`という接頭辞が邪魔なので、これを以下のように書き換えた。

```ruby:param_types.rb
class ParamType < ApplicationRecord
  has_many :values, class_name: "ParamValue"
end
```

```ruby:param_values.rb
class ParamValue < ApplicationRecord
  belongs_to :type, class_name: "ParamType", dependent: :destroy
end
```
# 問題
この状態で、
`type = ParamType.first`
とし、
`val = type.values.create(name:"ニカラグア", presentation: "nicalagua")`
としても、`rollback transaction`が発生してしまう。



```ruby:
[1] pry(main)> type = ParamType.first
  ParamType Load (0.1ms)  SELECT  "param_types".* FROM "param_types" ORDER BY "param_types"."id" ASC LIMIT ?  [["LIMIT", 1]]
+----+------+--------------+-------------------------+-------------------------+
| id | name | presentation | created_at              | updated_at              |
+----+------+--------------+-------------------------+-------------------------+
| 1  | 生産国  | country      | 2019-03-29 05:14:26 UTC | 2019-03-29 05:14:26 UTC |
+----+------+--------------+-------------------------+-------------------------+
1 row in set

[2] pry(main)> val = type.values.create(name: "ニカラグア", presentation: "nicalagua")
   (0.1ms)  begin transaction
   (0.1ms)  rollback transaction
```


# 対処1　[create!でエラーメッセージを確認]

`create`ではなく`create!`を使って例外を発生させると
エラーメッセージを確認できるという記事を見かけたため試すことに。


```ruby:
[3] pry(main)> val = type.values.create!(name: "ニカラグア", presentation: "nicalagua")
   (0.1ms)  begin transaction
   (0.1ms)  rollback transaction
ActiveRecord::RecordInvalid: バリデーションに失敗しました: Typeを入力してください
from /Users/owner/dev/cofapp/vendor/bundle/ruby/2.5.0/gems/activerecord-5.2.2.1/lib/active_record/validations.rb:80:in `raise_validation_error'
```

`Typeを入力してください`とあるため、`param_type_id`が入力されていないのか？と考える。

# 対処2　[`belongs_to :type`をいったん元に戻してみる]

そこで、`belongs_to :type`の部分のみ元に戻して確かめてみた。


```ruby:param_values.rb
class ParamValue < ApplicationRecord
  belongs_to :param_type, dependent: :destroy
end
```

とした状態でコマンドをたたくと、

```ruby:
[1] pry(main)> type = ParamType.first
  ParamType Load (0.1ms)  SELECT  "param_types".* FROM "param_types" ORDER BY "param_types"."id" ASC LIMIT ?  [["LIMIT", 1]]
+----+------+--------------+-------------------------+-------------------------+
| id | name | presentation | created_at              | updated_at              |
+----+------+--------------+-------------------------+-------------------------+
| 1  | 生産国  | country      | 2019-03-29 05:14:26 UTC | 2019-03-29 05:14:26 UTC |
+----+------+--------------+-------------------------+-------------------------+
1 row in set
[2] pry(main)> val = type.values.create!(name: "ニカラグア", presentation: "nicalagua")
   (0.1ms)  begin transaction
  ParamValue Create (0.5ms)  INSERT INTO "param_values" ("name", "presentation", "param_type_id", "created_at", "updated_at") VALUES (?, ?, ?, ?, ?)  [["name", "ニカラグア"], ["presentation", "nicalagua"], ["param_type_id", 1], ["created_at", "2019-03-29 05:57:07.416179"], ["updated_at", "2019-03-29 05:57:07.416179"]]
   (1.1ms)  commit transaction
+----+-------+--------------+---------------+-------------------------+-------------------------+
| id | name  | presentation | param_type_id | created_at              | updated_at              |
+----+-------+--------------+---------------+-------------------------+-------------------------+
| 5  | ニカラグア | nicalagua    | 1             | 2019-03-29 05:57:07 UTC | 2019-03-29 05:57:07 UTC |
+----+-------+--------------+---------------+-------------------------+-------------------------+
```

登録された。

やはり、`belongs_to :type`の記述がおかしかったと言える。


# 対処3　[`belongs_to :type`に戻し、`id`を指定]

`class_name`を指定するだけではダメだとわかった。
通常`belongs_to :xxxx`は`xxxx_id`を参照するが、
今回参照名を変更したので`xxxx_id`を指定してあげる。

```ruby:
class ParamValue < ApplicationRecord
  belongs_to :type, class_name: "ParamType", dependent: :destroy, source: :param_type
end
```

`has_many`時にも使用した`source:`を使用。

すると

```ruby:
[1] pry(main)> type = ParamType.first
  ParamType Load (0.1ms)  SELECT  "param_types".* FROM "param_types" ORDER BY "param_types"."id" ASC LIMIT ?  [["LIMIT", 1]]
+----+------+--------------+-------------------------+-------------------------+
| id | name | presentation | created_at              | updated_at              |
+----+------+--------------+-------------------------+-------------------------+
| 1  | 生産国  | country      | 2019-03-29 05:14:26 UTC | 2019-03-29 05:14:26 UTC |
+----+------+--------------+-------------------------+-------------------------+
1 row in set
[2] pry(main)> val = type.values.create!(name: "ニカラグア", presentation: "nicalagua")
ArgumentError: Unknown key: :source. Valid keys are: :class_name, :anonymous_class, :foreign_key, :validate, :autosave, :foreign_type, :dependent, :primary_key, :inverse_of, :required, :polymorphic, :touch, :counter_cache, :optional, :default
from /Users/reiforu/dev/cofapp/vendor/bundle/ruby/2.5.0/gems/activesupport-5.2.2.1/lib/active_support/core_ext/hash/keys.rb:81:in `block in assert_valid_keys'
```

`source:`は使えないと表示された。（`Valid keys:`に書いてあるシンボルは使用できる）

# 対処3　[`source:`から`foreign_key:`に変更]

`belongs_to`のリファレンスで調べてみると、どうやら
<b>
`has_many`には`source:`
`belongs_to`には`foreign_key:`
</b>
が対応するらしい。

てことで、以下のように書き換えた。

```ruby:param_values.rb
class ParamValue < ApplicationRecord
  belongs_to :type, class_name: "ParamType", source: :param_type_id, dependent: :destroy
end
```

すると

```ruby:
[1] pry(main)> type = ParamType.first
  ParamType Load (0.1ms)  SELECT  "param_types".* FROM "param_types" ORDER BY "param_types"."id" ASC LIMIT ?  [["LIMIT", 1]]
+----+------+--------------+-------------------------+-------------------------+
| id | name | presentation | created_at              | updated_at              |
+----+------+--------------+-------------------------+-------------------------+
| 1  | 生産国  | country      | 2019-03-29 05:14:26 UTC | 2019-03-29 05:14:26 UTC |
+----+------+--------------+-------------------------+-------------------------+
1 row in set
[2] pry(main)> val = type.values.create!(name: "ケニア", presentation: "kenya")
   (0.1ms)  begin transaction
  ParamType Load (0.1ms)  SELECT  "param_types".* FROM "param_types" WHERE "param_types"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
  ParamValue Create (0.6ms)  INSERT INTO "param_values" ("name", "presentation", "param_type_id", "created_at", "updated_at") VALUES (?, ?, ?, ?, ?)  [["name", "ケニア"], ["presentation", "kenya"], ["param_type_id", 1], ["created_at", "2019-03-29 06:11:38.968843"], ["updated_at", "2019-03-29 06:11:38.968843"]]
   (1.7ms)  commit transaction
+----+------+--------------+---------------+-------------------------+-------------------------+
| id | name | presentation | param_type_id | created_at              | updated_at              |
+----+------+--------------+---------------+-------------------------+-------------------------+
| 6  | ケニア  | kenya        | 1             | 2019-03-29 06:11:38 UTC | 2019-03-29 06:11:38 UTC |
+----+------+--------------+---------------+-------------------------+-------------------------+
```
となり、登録できた。

# 結論

`has_many`で参照名を変える場合は、

```ruby:
  has_many :yyyy, class_name: "Xxxx"
```
とすればOKだったが、

`belongs_to`で参照名を変える場合には、

```ruby:
  belongs_to :yyyy, class_name: "Xxxx", foreign_key: "xxxx_id"
```

としなければならないようだ。