What's the right approach to Ecto changesets?

Hello everybody,

I’ve been trying my hand at Elixir recently. And I was wondering what’s the right approach to using Ecto changetsets in the context of later having new data to add to it.

Since it seems once a changeset has been marked as invalid because of a failed rule, it’s impossible to make it revalidate (is it possible? I couldn’t find a function for it).

So my question is as follows:

Is it the right approach to just create a new changeset and run the existing + new data through it instead of trying to add to an existing changeset?

1 Like

That is correct!

1 Like

Thank you Ben.

Is it noted anywhere in the Ecto docs?
I don’t mind contributing to it, just want to know if it’s mentioned anywhere or worth mentioning it.

You can edit the fields of the Changeset, which mark it as invalid and which are documented (:valid?, :errors, :action). But maybe you will encounter other problems doing so, depends what you want to do.

If you just set the :action to nil, the errors in the form for example disappear. Again, I don’t know precisely your goal.

See also this discussion, if you are trying to copy an existing entity for insertion: https://groups.google.com/g/elixir-ecto/c/WGm4wNA7_6I/m/pFgeBdNoAQAJ?pli=1

1 Like

As @benwilson512 mentioned the approach you mention is ideal. But as @thojanssens1 points out it may depend on your specific case.

Probably not as idiomatic, but you’re always free to validate data and make changes to it as you see fit. For example:

def change_name(changeset, name) do
  # You may want or not check for changeset.valid? before transformation

  # Get the name or nil if is not on the changeset
  name = Ecto.Changeset.get_field(changeset, :name, nil)

  # If name is nil put the passed name parameter
  if name do
    changeset
  else 
    Ecto.Changeset.put_field(changeset, name)
  end
end

In the same way you’re also free to modify changeset’s struct by using Map.put or similar functions.

1 Like

I’m currently working with LiveView and I was storing the changeset in the socket and trying to evolve it with new changes whenever the user typed something in the form fields (which wasn’t working as expected).

Now, with all the info here what I do is reconstruct the changeset on every form change and store it in the socket to show any validation errors.

Thanks everybody!

2 Likes

Had very similar issues for a multiple-step form based layout. In the end I decided to use my own datastructure instead of the changeset to keep the previous structure state.

This way the changeset’s only role is form validation and not state-tracking :next_track_button: :previous_track_button: