Ecto validation for integer type?

I need to validate a field staff_id of type integer, but I always get valid? true for the changeset, even if I pass a string.

for example:

Schools.Teacher.changeset %Schools.Teacher{school_id: 1, name: "Sara", staff_id: "string here" }

then I get:

#Ecto.Changeset<action: nil, changes: %{}, errors: [],  data: #Schools.Teacher<>, valid?: true>

Teacher model:

defmodule Schools.Teacher do
  use Schools.Web, :model
  import Ecto.Changeset

  schema "teachers" do
    field :name, :string
    field :mobile, :string
    field :is_assistants, :boolean
    field :subject, :string
    field :school_id, :integer
    field :staff_id, :integer
    field :created_at, Ecto.DateTime
  end

  def changeset(teacher, params \\ %{}) do

    types = %{school_id: :integer, mobile: :string, name: :string, staff_id: :integer}

    {teacher, types}
    |> cast(params,[:name, :mobile, :school_id, :staff_id])
    |> unique_constraint(:name, name: :teachers_name_school_id_unique)
    
  end
end

Ofcourse, I get error when I try to insert into db:

Repo.insert Schools.Teacher.changeset %Schools.Teacher{school_id: 1, name: "Sara", staff_id: "dd" }
** (Ecto.ChangeError) value `"dd"` for `Schools.Teacher.staff_id` in `insert` does not match type :integer
    (ecto) lib/ecto/repo/schema.ex:706: Ecto.Repo.Schema.dump_field!/6
    (ecto) lib/ecto/repo/schema.ex:715: anonymous fn/6 in Ecto.Repo.Schema.dump_fields!/5
    (stdlib) lists.erl:1263: :lists.foldl/3
    (ecto) lib/ecto/repo/schema.ex:713: Ecto.Repo.Schema.dump_fields!/5
    (ecto) lib/ecto/repo/schema.ex:662: Ecto.Repo.Schema.dump_changes!/6
    (ecto) lib/ecto/repo/schema.ex:200: anonymous fn/13 in Ecto.Repo.Schema.do_insert/4

Why I did not get handled error? ex: {:error, error_message}? instead, a crash like above
And why Eco changeset has not validated the string passed to the integer field?

2 Likes

When you call: changeset(teacher), its the same as: changeset(teacher, %{}), so there are no „new” fields to run validations on and Ecto assumes the teacher is already valid.

Try: changeset(teacher_without_school_id, %{school_id: „bad”})

5 Likes

In other words: Changeset do apply their logic to changes, but not to the initial data being given with the only outlier being validate_required, which does check in both places if a field is set (which is to be expected). Changesets work under the assumption, that whatever data you’re supplying does already adhere to your business rules and is therefore clean, while the changes are coming from an untrusted source and must be validated.

4 Likes

@LostKobrakai Thanks for explanation

1 Like