Hi there!
I have a CRUD api for model that requires two different behaviours for missing data. For example, when IBAN is missing I should save NULL in the database, but when I have second line of address empty, I should save empty string.
I thought that using defaults
could be a suitable solution.
#migration
create table(:my_models) do
add :iban, :string, null: true
...
add :address2, :string, null: false
end
#schema
schema "my_models" do
field :iban, :string
...
field :address2, :string, default: ""
end
The strange behavior is that when I try to use my changeset with params like this:
%{"iban" => "", "address2" => ""}
I get changeset as expected with %{iban: nil, address2: ""}
Default empty_values
have empty string in them, so both iban and address2 are correctly changed to their default values. However for this:
%{"iban" => "", "address2" => nil} #edited
I would expect the same, but I get %{iban: nil, address2: nil}
. I checked Ecto.Changeset
code and default values are used only when field is in a list of empty values, but not when it is nil
. What is the reasoning behind it? Or is it just an oversight and nil
should be in a default list of empty values?
If I understand this correctly: address2 is not sent and we are still making it nil
based on the default value in the struct? If so, I would say that’s a bug, we should only apply the empty_values to parameters fields.
I am sorry, Looks like I was passing the nil
in explicit way. (Edited original question) and I didn’t make clear that I am trying to insert it to the database.
To reiterate I have a field address2
with default value ""
and there are couple of cases:
1.
- I have some data in parameters
params = %{"address2" => "some address line"}
- I create changeset and it sets the address field correctly
%{changes: %{address2: "some address line"}}
- I insert it to the database and it works as expected
-
- I am passing there empty string in parameters
params = %{"address2" => ""}
- Changeset checks that empty string is in
empty_values
, so it is not in changes %{changes: %{}}
- I insert to the database and
address2
has value of empty string, because of default
-
I don’t pass the parameter at all params = %{}
Works exactly like point 2.
-
I pass nil
as a parameter value: params = %{"address2" => nil}
-
nil
is not in empty_values
, so it is not removed from changeset, %{changes: {address2: nil}}
- when I insert to database,
address2
is set to NULL
.
Because of case 4. I was wondering why nil
isn’t one of the empty_values
by default. That would make empty string in parameters equivalent to nil in parameters. %{"field" => ""}
and %{"field" => nil}
would be the same.
While writing this reply I realized that empty values are just for string representations so putting there nil by default wouldn’t make sense.
In summary: there is no bug, everything works, I just go confused