Search code examples
elixirectochangeset

Ecto Changeset, required fields not validating on changes


learning Elixir/Ecto, and have hit a snag related to changesets. Not sure what I'm doing wrong.

I have a "domain model" struct that has some required fields. When I do a put_change, the returned changeset is still saying there's an error on the changeset (field missing), even though it is right there in the changes.

cset = Activity.changeset(%Activity{}, %{details: "Played in the snow", child_id: child_id}) 

#Ecto.Changeset<action: nil,
 changes: %{child_id: "ed553c30-38d2-4cb1-9029-eb2180c141cc",
   details: "Played in the snow"},
 errors: [relevant_date: {"can't be blank", [validation: :required]},
  display_time: {"can't be blank", [validation: :required]}],
 data: #MyApp.Domain.Activity<>, valid?: false> 

The above is to be expected. both relevant_date and display_time are missing, and as such the errors should be there.

cset |> Ecto.Changeset.put_change(:relevant_date, ~D[2016-12-31])

#Ecto.Changeset<action: nil,
 changes: %{child_id: "ed553c30-38d2-4cb1-9029-eb2180c141cc",
   details: "Played in the snow", relevant_date: ~D[2016-12-31]},
 errors: [relevant_date: {"can't be blank", [validation: :required]},
  display_time: {"can't be blank", [validation: :required]}],
 data: #Kidgenius.Domain.Activity<>, valid?: false>

This is the part that doesn't make any sense to me. relevant_date is right there on the changes field, and yet it's still telling me that relevant_date can't be blank.

Any help would be appreciated!

EDIT: This is Ecto 2.1.1.


Solution

  • put_change doesn't perform validation, you need to pass updated changeset through validate_required, so it will try to perform validation on your updated state.

    Hope that clarifies the problem a bit!

    EDIT

    Just for clarification:

    # 1.
    cset =
      Activity.changeset(
        %Activity{},
        %{details: "Played in the snow", child_id: child_id}
      )
    
    # 2.
    cset = Ecto.Changeset.put_change(cset, :relevant_date, ~D[2016-12-31])
    
    # 3. This probably is something you currently have implemented
    #    in your `Activity.changeset`, most likely as second setep
    #    after `Ecto.cast/3`.
    cset = Ecto.Changeset.validate_required(cset, [:relevant_date])