1つのモデルに別の1つのモデルを複数個持たせる方法
rails5にて[Rails] 同じmodelを参照する外部キーを一つのmodelでもつ方法と
同様のことを実施したらdbへの登録時に
ActiveRecord::StatementInvalid: SQLite3::SQLException: no such table: main.receivers:
エラーが出てハマったので備忘録
環境
ruby 2.3.5
rails 5.2.3
やりたいこと
- MemberモデルとMoneyRecordモデルがある
- MoneyRecordモデルに送り主(Sender)と受取人(Receiver)を持たせる
- SenderとReceiverはMemberモデルと紐づける
dbは以下の通り
members
カラム名 | 型 |
---|---|
id | integer/primary |
member_name | string |
created_at | timestamp |
updated_at | timestamp |
money_records
カラム名 | 型 |
---|---|
id | integer/primary |
sender_id | FK |
receiver_id | FK |
created_at | timestamp |
updated_at | timestamp |
解決法
マイグレーションファイル
マイグレーションファイルに to_table
オプションをつけることで
参照先テーブル名を明示的に指定してあげればできます。
参照:マイグレーションにおいて参照先テーブル名を自動で推定できないカラムを外部キーとして指定する方法
money_recordsのsenderとreceiverの foreign_key: true
を以下に書き換え
create_money_records.rb
class CreateMoneyRecords < ActiveRecord::Migration[5.2]
def change
create_table :money_records do |t|
t.references :sender, foreign_key: { to_table: :members }
t.references :receiver, foreign_key: { to_table: :members }
t.timestamps
end
end
end
※membersのマイグレーションはデフォルトでOK
create_member.rb
class CrceateMembers < ActiveRecord::Migration[5.2]
def change
create_table :members do |t|
t.string :member_name
t.timestamps
end
end
end
モデル
Memberモデルからはクラス名と外部キーを、MoneyRecordモデルからはクラス名を指定してあげる。
member.rb
class Member < ApplicationRecord
has_many :sender_records, class_name: 'MoneyRecord', foreign_key: 'sender_id'
has_many :receiver_records, class_name: 'MoneyRecord', foreign_key: 'receiver_id'
end
money_record.rb
class MoneyRecord < ApplicationRecord
belongs_to :sender, class_name: 'Member'
belongs_to :receiver, class_name: 'Member'
end
結果
登録
pry(main)> member_1 = Member.new(member_name: "hoge")
=> #<Member:0x00007f0db4c23e68 id: nil, member_name: "hoge", created_at: nil, updated_at: nil>
pry(main)> member_1.save
(0.1ms) begin transaction
Member Create (3.1ms) INSERT INTO "members" ("member_name", "created_at", "updated_at") VALUES (?, ?, ?) [["member_name", "hoge"], ["created_at", "2019-09-26 03:08:49.593452"], ["updated_at", "2019-09-26 03:08:49.593452"]]
(3.6ms) commit transaction
=> true
pry(main)> member_2 = Member.new(member_name: "fuga")
=> #<Member:0x00007f0db4b0d420 id: nil, member_name: "fuga", created_at: nil, updated_at: nil>
pry(main)> member_2.save
(0.1ms) begin transaction
Member Create (5.2ms) INSERT INTO "members" ("member_name", "created_at", "updated_at") VALUES (?, ?, ?) [["member_name", "fuga"], ["created_at", "2019-09-26 03:09:47.400686"], ["updated_at", "2019-09-26 03:09:47.400686"]]
(4.0ms) commit transaction
=> true
[6] pry(main)> mr = MoneyRecord.new(sender: member_1, receiver: member_2)
=> #<MoneyRecord:0x00007f0db497ed48 id: nil, sender_id: 1, receiver_id: 2, created_at: nil, updated_at: nil>
[7] pry(main)> mr.save
(0.1ms) begin transaction
MoneyRecord Create (2.2ms) INSERT INTO "money_records" ("sender_id", "receiver_id", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["sender_id", 1], ["receiver_id", 2], ["created_at", "2019-09-26 03:10:10.874060"], ["updated_at", "2019-09-26 03:10:10.874060"]]
(4.0ms) commit transaction
=> true
# 無事登録できた!
参照
# MoneyRecord -> Member の参照
pry(main)> mr.sender
=> #<Member:0x00007f0db4c23e68 id: 1, member_name: "hoge", created_at: Thu, 26 Sep 2019 03:08:49 UTC +00:00, updated_at: Thu, 26 Sep 2019 03:08:49 UTC +00:00>
pry(main)> mr.receiver
=> #<Member:0x00007f0db4b0d420 id: 2, member_name: "fuga", created_at: Thu, 26 Sep 2019 03:09:47 UTC +00:00, updated_at: Thu, 26 Sep 2019 03:09:47 UTC +00:00>
# 参照できてた!
# Member -> MoneyRecord の参照
pry(main)> member_1.sender_records[0]
=> #<MoneyRecord:0x00007f0db4625fc0 id: 1, sender_id: 1, receiver_id: 2, created_at: Thu, 26 Sep 2019 03:10:10 UTC +00:00, updated_at: Thu, 26 Sep 2019 03:10:10 UTC +00:00>
pry(main)> member_2.receiver_records[0]
=> #<MoneyRecord:0x00007f0db459c1f8 id: 1, sender_id: 1, receiver_id: 2, created_at: Thu, 26 Sep 2019 03:10:10 UTC +00:00, updated_at: Thu, 26 Sep 2019 03:10:10 UTC +00:00>
# 参照できてた!