なんのこっちゃ? タイトルを上手く書けなかったので、ER図で説明します。
下記のようにtask_cyclesにspace_idを入れなくてもtasksを経由すれば引けるのですが、DBのindexを効率的に利かせたいのと、保守のし易さ(単体で対象が分かる)ので、あえて追加しました。
→ SchemaSpyでER図とDB設計書を自動生成する
→ SchemaSpyを既存アプリのDockerに導入する
普通にassociation入れると
spec/factories/task_cycles.rb
FactoryBot.define do factory :task_cycle do association :space association :task end end
spec/factories/tasks.rb
FactoryBot.define do factory :task do association :space end end
Spaceがそれぞれ作られて、space_idが違う値になってしまう。
% rails c -s > task_cycle = FactoryBot.create(:task_cycle) => #<TaskCycle id: 798, space_id: 147, task_id: 99, ... > task_cycle.task => #<Task id: 99, space_id: 148, ...
コールバックで対応
spec/factories/task_cycles.rb
FactoryBot.define do factory :task_cycle do association :space - association :task + after(:build) do |task_cycle| + task_cycle.task = FactoryBot.build(:task, space: task_cycle.space) if task_cycle.task.blank? + end + after(:stub) do |task_cycle| + task_cycle.task = FactoryBot.build_stubbed(:task, space: task_cycle.space) if task_cycle.task.blank? + end end end
Spaceを使い回して、space_idが同じ値になる!
% rails c -s > task_cycle = FactoryBot.create(:task_cycle) => #<TaskCycle id: 799, space_id: 149, task_id: 100, ... > task_cycle.task => #<Task id: 100, space_id: 149, ...
before(:create)はafter(:build)で対応できるので不要。
after(:create)はDB保存後なので、not nullでエラーになる。
参考: コールバックを受ける: FactoryBot(FactoryGirl)チートシート – Qiita
更に改良
task指定だと、space_idが異なってしまう。
> task = FactoryBot.create(:task) => #<Task id: 108, space_id: 156, ... > task_cycle = FactoryBot.create(:task_cycle, task: task) => #<TaskCycle id: 807, space_id: 157, task_id: 108, ...
spec/factories/task_cycles.rb
FactoryBot.define do factory :task_cycle do - association :space after(:build) do |task_cycle| - task_cycle.task = FactoryBot.build(:task, space: task_cycle.space) if task_cycle.task.blank? + if task_cycle.task.blank? + task_cycle.space = FactoryBot.build(:space) if task_cycle.space.blank? + task_cycle.task = FactoryBot.build(:task, space: task_cycle.space) + else + task_cycle.space = task_cycle.task.space + end end after(:stub) do |task_cycle| - task_cycle.task = FactoryBot.build_stubbed(:task, space: task_cycle.space) if task_cycle.task.blank? + if task_cycle.task.blank? + task_cycle.space = FactoryBot.build_stubbed(:space) if task_cycle.space.blank? + task_cycle.task = FactoryBot.build_stubbed(:task, space: task_cycle.space) if task_cycle.task.blank? + else + task_cycle.space = task_cycle.task.space + end end end end
task.spaceをセットして、space_idが同じ値になる!
> task = FactoryBot.create(:task) => #<Task id: 109, space_id: 158, ... > task_cycle = FactoryBot.create(:task_cycle, task: task) => #<TaskCycle id: 808, space_id: 158, task_id: 109, ...
他も動作確認
> space = FactoryBot.create(:space) => #<Space id: 152, > task_cycle = FactoryBot.create(:task_cycle, space: space) => #<TaskCycle id: 802, space_id: 152, task_id: 103, ... > task_cycle.task => #<Task id: 103, space_id: 152, ...
> task_cycles = FactoryBot.create_list(:task_cycle, 2, space: space) => [#<TaskCycle id: 803, space_id: 152, task_id: 104, ... > task_cycles.second => #<TaskCycle id: 804, space_id: 152, task_id: 105, ...
> task_cycle = FactoryBot.build(:task_cycle) => #<TaskCycle id: nil, space_id: nil, task_id: nil, ... > task_cycle.task => #<Task id: nil, space_id: nil, ...
> task_cycle = FactoryBot.build_stubbed(:task_cycle) => #<TaskCycle id: 1003, space_id: 1002, task_id: 1005, ... > task_cycle.task => #<Task id: 1005, space_id: 1002, ...
> task = FactoryBot.build_stubbed(:task) => #<Task id: 1009, space_id: 1007 ... > task_cycle = FactoryBot.build_stubbed(:task_cycle, task: task) => #<TaskCycle id: 1010, space_id: 1007, task_id: 1009 ...
“FactoryBot:リレーション先でリレーション元と同じidで作成されるようにする” に対して1件のコメントがあります。