Ecto 3.12.4 on Elixir 1.17.3
After significant playing around with Ecto with fields defined with writable: :insert
I am confused. Here is my scenario:
I had a field defined as writable: :insert which was because I wanted it to be set only on create and never changed. Unfortunately this was an issue for one particular use case I had. I won’t go into the use cases details because they are not relevant.
What is relevant is that the code with the field set to writable: :insert was returning from the update of that struct with the field set to the new value even though that new value was never persisted.
Code snippet
case Mutate.create_client(altered_params) do
{:ok, client1} ->
IO.inspect(client1)
#--> %Sct.Network.Client{
__meta__: #Ecto.Schema.Metadata<:loaded, "collectives">,
id: 1603900,
name: "aaa33",
permalink: "2e3cdcfe-d082-4e4b-bb2a-469e35b63bf9",
type: "Organization",
master_client_id: 1603893,
created_at: ~U[2024-10-28 21:33:16.077693Z],
updated_at: ~U[2024-10-28 21:33:16.077693Z]
}
# at this point the master_client_id has been set to a inserted value and clent1.master_client_id matches what is in database
{:ok, client2} =
Mutate.update_client(client1, %{
master_client_id: client1.id
})
#changeset => |> Client.changeset(Map.drop(attrs, [:id, :type, :eusage, :permalink])) #=> #Ecto.Changeset<
action: nil,
changes: %{master_client_id: 1603900},
errors: [],
data: #Sct.Network.Client<>,
valid?: true,
...
>
# this is where I get a bit confined. Client1.id (the input to my change) i
# s a different value then client1.master_client_id and with that
# field set to writable: :insert this does not alter the value in the database.
# That was expected (after I reviewed it) but the return result is
# categorized as ":ok" HOWEVER client2 now shows as
# having master_client_id ALTERED.
#=> {:ok,
%Sct.Network.Client{
__meta__: #Ecto.Schema.Metadata<:loaded, "collectives">,
id: 1603900,
name: "aaa33",
permalink: "2e3cdcfe-d082-4e4b-bb2a-469e35b63bf9",
type: "Organization",
master_client_id: 1603900,
created_at: ~U[2024-10-28 21:33:16.077693Z],
updated_at: ~U[2024-10-28 21:33:16.077693Z]
}}
IO.inspect(client2)
%Sct.Network.Client{
__meta__: #Ecto.Schema.Metadata<:loaded, "collectives">,
id: 1603900,
name: "aaa33",
master_client_id: 1603900,
created_at: ~U[2024-10-28 21:33:16.077693Z],
updated_at: ~U[2024-10-28 21:33:16.077693Z]
}
true = client2.master_client_id == client2.id
persisted_client = Sct.Client.Query.get_client!(client2.id)
IO.inspect(persisted_client)
%Sct.Network.Client{
__meta__: #Ecto.Schema.Metadata<:loaded, "collectives">,
id: 1603900,
name: "aaa33",
master_client_id: 1603893,
created_at: ~U[2024-10-28 21:33:16.077693Z],
updated_at: ~U[2024-10-28 21:33:16.077693Z]
}
So I am either confused about why I am getting a changeset that says it is valid? when I am attempting to update an insert only field ( or )
I am confused about what an “:ok” returned value from an Ecto Update means when it has altered the struct but never persisted that alteration, in fact in this case NEVER made a DB request at all!!!
It seems like one or the other of these outcomes destroys trust in the responses from Ecto???
Help me resolve this confusion please.