なんのこっちゃ? タイトルを上手く書けなかったので、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 ...

今回のコミット内容
https://dev.azure.com/nightonly/nightonly-app/_git/nightonly-rails/commit/f385dd3d8dbca84af3f1d0c6b440577caa1c8a13

FactoryBot:リレーション先でリレーション元と同じidで作成されるようにする” に対して1件のコメントがあります。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です