Why are parent's has_many assocs not updated after Repo.update(child)

I have these tables

create table(:childs) do
  add(:name, :string)
  add(:parent_id, references(:parents, on_delete: :nilify_all))
end

create table(:parents) do
  add(:name, :string)
end

with these schemas

schema "childs" do
  field :name, :string
  belongs_to :parent, Parent
end

schema "parents" do
  field :name, :string
  has_many :children, Child
end

why does this not work?

parent = Repo.insert!(%Parent{name: "parent"})
child_1 = Repo.insert!(%Child{name: "child_1", parent: parent})
child_2 = Repo.insert!(%Child{name: "child_2"})

{:ok, child_2} = %{child_2 | parent: parent} |> cast(%{}, []) |> Repo.update()
parent = Repo.get_by!(Parent, name: "parent") |> Repo.preload(:children)

the parent does not see its new child :frowning:

parent #=> %ToolDbPoc.Schema.Parent{
  __meta__: #Ecto.Schema.Metadata<:loaded, "parents">,
  id: 1,
  name: "parent",
  children: [
    %ToolDbPoc.Schema.Child{
      __meta__: #Ecto.Schema.Metadata<:loaded, "childs">,
      id: 1,
      name: "child_1",
      parent_id: 1,
      parent: #Ecto.Association.NotLoaded<association :parent is not loaded>
    }
  ]
}

[test/object_devices_test.exs:62: ToolDbPocTest.ObjectDevices."test demo"/1]
child_2 #=> %ToolDbPoc.Schema.Child{
  __meta__: #Ecto.Schema.Metadata<:loaded, "childs">,
  id: 2,
  name: "child_2",
  parent_id: nil,
  parent: %ToolDbPoc.Schema.Parent{
    __meta__: #Ecto.Schema.Metadata<:loaded, "parents">,
    id: 1,
    name: "parent",
    children: #Ecto.Association.NotLoaded<association :children is not loaded>
  }
}

I think this is the explanation:

A changeset is required as it is the only mechanism for tracking dirty changes. Only the fields present in the changes part of the changeset are sent to the database. Any other, in-memory changes done to the schema are ignored.

https://hexdocs.pm/ecto/Ecto.Repo.html#c:update/2

Why not do this?

child_2 |> change(parent: parent) |> Repo.update()
1 Like

being stupid. thanks.