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

  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)

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?


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”})


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.


@LostKobrakai Thanks for explanation

1 Like