From 1fad133608c1e5ff5782fa3a9ba37861d8733339 Mon Sep 17 00:00:00 2001 From: Lukasz Samson Date: Sat, 18 Jul 2026 09:05:32 +0200 Subject: [PATCH 1/2] Fix stale belongs_to key in apply_changes --- lib/ecto/changeset.ex | 9 ++++++--- test/ecto/changeset_test.exs | 4 +++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/ecto/changeset.ex b/lib/ecto/changeset.ex index f79066955f..7a8acf140a 100644 --- a/lib/ecto/changeset.ex +++ b/lib/ecto/changeset.ex @@ -4323,9 +4323,12 @@ defmodule Ecto.Changeset do acc = Map.put(acc, key, relation_changed) - with %Ecto.Association.BelongsTo{related_key: related_key} <- relation, - %{^related_key => id} <- relation_changed do - Map.put(acc, relation.owner_key, id) + with %Ecto.Association.BelongsTo{related_key: related_key, owner_key: owner_key} <- relation do + case relation_changed do + %{^related_key => id} -> Map.put(acc, owner_key, id) + nil -> Map.put(acc, owner_key, nil) + _ -> acc + end else _ -> acc end diff --git a/test/ecto/changeset_test.exs b/test/ecto/changeset_test.exs index 3f1b7910b6..d4b2054ead 100644 --- a/test/ecto/changeset_test.exs +++ b/test/ecto/changeset_test.exs @@ -1083,8 +1083,10 @@ defmodule Ecto.ChangesetTest do assert changed_post.title == "foo" assert changed_post.category_id == category.category_id + post_with_category = %Post{category_id: 5} + changeset = - post + post_with_category |> changeset(%{"title" => "foo"}) |> put_assoc(:category, nil) From 5f1a69294eea73f151abd4c29b4dcf97fe17dd21 Mon Sep 17 00:00:00 2001 From: Lukasz Samson Date: Sat, 18 Jul 2026 09:24:49 +0200 Subject: [PATCH 2/2] Enforce belongs_to relation invariant in apply_changes --- lib/ecto/changeset.ex | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/lib/ecto/changeset.ex b/lib/ecto/changeset.ex index 7a8acf140a..09dd81ebb4 100644 --- a/lib/ecto/changeset.ex +++ b/lib/ecto/changeset.ex @@ -4323,14 +4323,13 @@ defmodule Ecto.Changeset do acc = Map.put(acc, key, relation_changed) - with %Ecto.Association.BelongsTo{related_key: related_key, owner_key: owner_key} <- relation do - case relation_changed do - %{^related_key => id} -> Map.put(acc, owner_key, id) - nil -> Map.put(acc, owner_key, nil) - _ -> acc - end - else - _ -> acc + case relation do + %Ecto.Association.BelongsTo{related_key: related_key, owner_key: owner_key} -> + id = relation_changed && Map.fetch!(relation_changed, related_key) + Map.put(acc, owner_key, id) + + _ -> + acc end end