1
votes

I'm experimenting with Ecto and I have issues with validating data in the changeset() function.

The Schema is as follows:

defmodule Barakuda.TestData do
  use Ecto.Schema

  schema "test_data" do
    field :username, :string
    field :age, :integer
  end

  def changeset(data, params \\ %{}) do
    data
    |> Ecto.Changeset.cast(params, [:username, :age])
    |> Ecto.Changeset.validate_required([:username, :age])
  end

end

Now lets try with invalid data:

iex(125)> d1=%Barakuda.TestData{id: 1, username: "polo"}
%Barakuda.TestData{__meta__: #Ecto.Schema.Metadata<:built, "test_data">,
 age: nil, id: 1, username: "polo"}
iex(126)> Barakuda.TestData.changeset(d1).valid?        
false

That's ok as the age field is missing. The same happens if I remove the username. Fine!

Now, I add the following line at the end of changeset() (and, yes, I recompiled):

data
  |> Ecto.Changeset.cast(params, [:username, :age])
  |> Ecto.Changeset.validate_required([:username, :age])
  |> Ecto.Changeset.validate_number(:age, less_than: 20)

This is supposed to be true if age is strictly less than 20, e.i.: 19, 18, ... and false otherwise. Right? Lets give it a try:

iex(19)> d1=%Barakuda.TestData{id: 1, username: "polo", age: 15}
%Barakuda.TestData{__meta__: #Ecto.Schema.Metadata<:built, "test_data">,
 age: 15, id: 1, username: "polo"}
iex(20)> Barakuda.TestData.changeset(d1).valid?                 
true

which is OK. Yet

iex(130)> d1=%Barakuda.TestData{id: 1, username: "polo", age: 22}
%Barakuda.TestData{__meta__: #Ecto.Schema.Metadata<:built, "test_data">,
 age: 22, id: 1, username: "polo"}
iex(131)> Barakuda.TestData.changeset(d1).valid?                 
true

The same actually happens for other validate_* functions as for example (with or without count: :codepoints):

Ecto.Changeset.validate_length(:username, min: 6, count: :codepoints)

So, what am I doing wrong ?

NB: Elixir 1.5.1 and Ecto v2.2.6 (2017-09-30)

1

1 Answers

4
votes

validate_length does not check existing fields, only "changed" fields.

validate_length(changeset, field, opts)

Validates a change is a string or list of the given length.

Source

Since you're calling Barakuda.TestData.changeset with all the fields in the struct and nothing in params argument, no field is marked as "changed" by Ecto and validate_length does nothing. The correct way to do this is to pass the existing struct (with default/existing values) as the first argument and all the additions that need to be validated as the second argument, params. The following code should return false for you:

changeset = Barakuda.TestData.changeset(%Barakuda.TestData{}, %{id: 1, username: "polo", age: 22})
changeset.valid?