endymion
Changes not properly applied with Ecto.Changeset.apply_changes/1
I am wondering why when I ran Ecto.Changeset.apply_changes(changeset) the prepared changes function on that changeset did not run. I have a changeset that I am building from a Phoenix form; after validation occurs and user submits the form, I want to apply those params to my struct with a prepared change to increment a value of one of the inputs.
Simple example demonstrating the unexpected behavior of apply_changes/1
We have a sample struct. It casts some params then adds a prepare_changes function to that changeset. When calling apply_changes that prepare_changes function is not run so our resulting struct is incorrect.
Example:
defmodule InputData do
use Ecto.Schema
@primary_key false
embedded_schema do
field :value, :integer
end
end
%InputData{value: nil}
|> Ecto.Changeset.cast(%{"value" => "1"}, [:value])
|> Ecto.Changeset.validate_number(:value, [greater_than: 0])
|> Ecto.Changeset.prepare_changes(fn changeset ->
incremented_value = Ecto.Changeset.get_field(:value, 0) + 1
Ecto.Changeset.put_change(changeset, :value, incremented_value)
end)
|> Ecto.Changeset.apply_changes()
The output of this is not the expected %InputData{value: 2} where the params casts the value to 1 then is incremented again by 1 in the prepare_changes/2. Why is this? Since apply_changes/1 results in a struct and not a changeset there is no risk of firing the prepared_changes functions again if the developer needed to to a Repo insert. In my case I am never inserting this struct into a database and thus need these prepared_changes to run when calling apply_changes.
In summary this feels like a bug or missing implementation in Ecto Changesets that I seem unable to come up with a convincing argument as to why it shouldn’t behave like this.
Most Liked
LostKobrakai
The first sentence of the documentation states:
Provides a function executed by the repository on insert/update/delete.
For Ecto.Changeset.apply_changes there’s no repository involved and it’s neither of those mentioned actions either.
Usually prepare_changes is used to execute code within the same transaction as what the changeset applies to the database, which needs to be provided as a callback as that transaction is only started later (by the repo). If you don’t have a transaction you can directly apply the changes to the changeset in place. No need to delay that.
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance









