I am writting a method that reassigns a polymorphic collection like:
class Color < ApplicationRecord
belongs_to :colorable, polymorphic: true
end
class Table < ApplicationRecord
has_many :colors, as: :colorable
end
When I reasign the collection the existing elements of the collection have their owner connection fields nullified:
table.colors = new_colors_array
This query is executed:
UPDATE "colors" SET "colorable_id" = $1, "colorable_type" = $2 WHERE "colors"."colorable_id" = $3 AND "colors"."colorable_type" = $4 AND ("colors"."id" = $5 OR "colors"."id" = $6) [["colorable_id", nil], ["colorable_type", nil], ["colorable_id", "THE_TABLE_ID"], ["colorable_type", "Table"], ["id", "THE_COLOR_ID_1"], ["id", "THE_COLOR_ID_2"]]
And it causes this error:
ActiveRecord::NotNullViolation: PG::NotNullViolation: ERROR: null value in column "colorable_type" of relation "colors" violates not-null constraint
I could do first a:
table.colors.destroy_all
But the method that contains all this logic is supposed to not make any persistent change in the DB. Allowing developers to make table.save
when they are ready.
What would be the strategy to make a collection replacement but still not doing any persistent change in the DB until colorable.save
is called?
I have tried with:
table.colors.clear # => touches DB
table.colors = [] # => touches DB
table.colors.reset # => touches DB
table.color.replace(new_colors_array) # => touches DB
ActiveRecord actually has a method to manage these cases:
We can use it like this:
def assign_colors(new_colors)
colors.each do |color|
color.mark_for_destruction
end
new_colors.each do |new_color|
colors << new_color
end
end