Reputation: 1339
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)
Upvotes: 1
Views: 658
Reputation: 222158
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.
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?
Upvotes: 4