diff --git a/lib/ecto/changeset.ex b/lib/ecto/changeset.ex index 90368a887b..d68f334480 100644 --- a/lib/ecto/changeset.ex +++ b/lib/ecto/changeset.ex @@ -1549,6 +1549,8 @@ defmodule Ecto.Changeset do * `required` - required fields are merged; all the fields that appear in the required list of both changesets are moved to the required list of the resulting changeset. + * `prepare` - prepare callbacks are concatenated in the order of the + changesets being merged. ## Examples @@ -1574,6 +1576,7 @@ defmodule Ecto.Changeset do new_filters = Map.merge(cs1.filters, cs2.filters) new_validations = cs1.validations ++ cs2.validations new_constraints = cs1.constraints ++ cs2.constraints + new_prepare = cs2.prepare ++ cs1.prepare # They are always set, so they should never be nil _ = merge_identical(cs1.empty_values, cs2.empty_values, "empty values") @@ -1586,7 +1589,8 @@ defmodule Ecto.Changeset do filters: new_filters, action: new_action, validations: new_validations, - constraints: new_constraints + constraints: new_constraints, + prepare: new_prepare }, cs2 ) diff --git a/test/ecto/changeset_test.exs b/test/ecto/changeset_test.exs index 821140c40f..978b91790e 100644 --- a/test/ecto/changeset_test.exs +++ b/test/ecto/changeset_test.exs @@ -662,6 +662,18 @@ defmodule Ecto.ChangesetTest do assert length(constraints(changeset)) == 2 end + test "merge/2: merges prepare changes callbacks" do + data = %Post{} + cs1 = change(data) |> prepare_changes(&put_change(&1, :title, "Title")) + cs2 = change(data) |> optimistic_lock(:upvotes) + + for changeset <- [merge(cs1, cs2), merge(cs2, cs1)] do + assert length(changeset.prepare) == 2 + assert changeset.filters == %{upvotes: 0} + assert prepared_changes(changeset) == %{title: "Title", upvotes: 1} + end + end + test "merge/2: merges types" do cs1 = cast({%{}, %{title: :string}}, %{title: "foo"}, ~w(title)a) cs2 = cast({%{}, %{body: :string}}, %{body: "foo"}, ~w(body)a)