I faced with very similar problem as @grufino in his topic regarding the foreign key on nested models deletion.
I prepared a small sample code to show the problem.
I did exactly the same as written in the topic above and manually added changeset foreign_key_constraint/3 on changed has_many
model since in Relation.ex
file in Ecto has a such code {:ok, Changeset.change(changeset_or_struct) |> put_new_action(:replace)}
which doesn’t take any parameter to pass deletion changeset and always raises foreign_key exception.
The current test looks like
test "delete foreign key test", %{user_id: user_id} do
timetable = insert(:timetable, %{
user_id: user_id,
date_ranges: [
build(:today_date_range, %{
slots: [
build(:now_date_range_slot)
]
})
]
})
# Create foreign model to raise a FK exception.
_check = insert(:check, %{
date_range_id: List.first(timetable.date_ranges).id,
date_range_id: List.first(List.first(timetable.date_ranges).slots).id,
user_id: user_id
})
update_attrs = string_params_for(:timetable, %{
date_ranges: [
build(:tomorrow_date_range, %{
slots: [
build(:now_date_range_slot)
]
})
]
})
assert {:error, %Ecto.Changeset{} = changeset} = EctoDelete.TimetableManager.update_timetable(timetable, update_attrs)
assert errors_on(changeset) == %{}
end
And the output is
1) test delete foreign key test (EctoForeignDeleteAppTest)
test/ecto_foreign_delete_app_test.exs:12
Assertion with == failed
code: assert errors_on(changeset) == %{}
left: %{date_ranges: [%{timetable: ["You can't delete this date range. You have checks"]}, %{}]}
right: %{}
stacktrace:
test/ecto_foreign_delete_app_test.exs:42: (test)
The exception is gone and now I have this beautiful error. But as you can see date_ranges
has an empty %{}
in the list, so when call Ecto.Changeset.traverse_errors
it is considers inserted/updated schemas as affected, but they are not and shouldn’t have this map inside. The same is applicable on slot deletion(means next nesting level)
Any suggestions?