Skip to content

Commit d526c74

Browse files
authored
fix: recompute changed? after setup_managed_belongs_to_relationships (#2647)
When manage_relationship is called from before_transaction or before_action hooks on update actions, the FK attribute gets set via force_change_attribute inside setup_managed_belongs_to_relationships. However, the changed? flag was computed before this point, so it remained false, causing the DB update to be skipped entirely. The related record was created but the source record's FK was never persisted. Recompute changed? after setup_managed_belongs_to_relationships returns so that FK changes from managed relationships are detected.
1 parent 84f13b4 commit d526c74

2 files changed

Lines changed: 51 additions & 0 deletions

File tree

lib/ash/actions/update/update.ex

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -471,8 +471,22 @@ defmodule Ash.Actions.Update do
471471
{:error, error}
472472

473473
{changeset, manage_instructions} ->
474+
# Recompute changed? after setup_managed_belongs_to_relationships
475+
# may have set FK attributes via force_change_attribute (e.g. when
476+
# manage_relationship is called from before_transaction hooks on
477+
# update actions). Without this, the changed? flag computed before
478+
# the func callback would be false, causing the DB update to be
479+
# skipped entirely.
480+
changed? =
481+
Ash.Changeset.changing_attributes?(changeset) or
482+
not Enum.empty?(changeset.atomics)
483+
474484
changeset =
475485
changeset
486+
|> Ash.Changeset.put_context(
487+
:changed?,
488+
changeset.context[:changed?] || changed?
489+
)
476490
|> Ash.Changeset.require_values(
477491
:update,
478492
true

test/manage_relationship_test.exs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,22 @@ defmodule Ash.Test.ManageRelationshipTest do
252252

253253
change manage_relationship(:parent_resource, on_match: :update)
254254
end
255+
256+
update :update_create_parent_in_hook do
257+
require_atomic? false
258+
259+
change fn changeset, _context ->
260+
Ash.Changeset.before_transaction(changeset, fn changeset ->
261+
Ash.Changeset.manage_relationship(
262+
changeset,
263+
:parent_resource,
264+
%{name: "created-in-hook"},
265+
type: :create,
266+
on_no_match: :create
267+
)
268+
end)
269+
end
270+
end
255271
end
256272

257273
attributes do
@@ -1289,4 +1305,25 @@ defmodule Ash.Test.ManageRelationshipTest do
12891305
assert names == ["new1", "existing1", "new2", "existing2"]
12901306
end
12911307
end
1308+
1309+
describe "manage_relationship called from before_transaction hook on update" do
1310+
test "creates related record via belongs_to when manage_relationship is called in before_transaction" do
1311+
related =
1312+
RelatedResource
1313+
|> Ash.Changeset.for_create(:create, %{required_attribute: "test"})
1314+
|> Ash.create!()
1315+
1316+
assert related.parent_resource_id == nil
1317+
1318+
updated =
1319+
related
1320+
|> Ash.Changeset.for_update(:update_create_parent_in_hook, %{})
1321+
|> Ash.update!()
1322+
1323+
assert updated.parent_resource_id != nil
1324+
1325+
{:ok, loaded} = Ash.load(updated, :parent_resource)
1326+
assert loaded.parent_resource.name == "created-in-hook"
1327+
end
1328+
end
12921329
end

0 commit comments

Comments
 (0)