Consider the following schema:
defmodule EctoBug.Post do
use Ecto.Schema
import Ecto.Changeset
schema "posts" do
field :title, :string, default: "test"
timestamps()
end
def changeset(post, attrs) do
post
|> cast(attrs, [:title])
|> validate_required([:title])
end
end
If I do
changeset = EctoBug.Post.changeset(%EctoBug.Post{}, %{title: "test"})
title
field is not present in changes
:
#Ecto.Changeset<action: nil, changes: %{}, errors: [], data: #EctoBug.Post<>, valid?: true>
I couldn't find anything on this behavior.
Is it a bug?
Ecto's changeset structure holds changes applied to a schema/struct. The reason the default doesn't appear as a change is because when you do %EctoBug.Post{}
, it's populated with the default values you set on the schema. Then when you cast the params, since the original and the cast values for the field are the same, it's not really a change and isn't marked as such.
If you do
changeset = EctoBug.Post.changeset(%EctoBug.Post{}, %{title: "test"})
Ecto.Changeset.get_field(changeset, :title)
You should see that title is set. Although Ecto.Changeset.get_change(changeset, :title)
will give you nil since it hasn't been changed from the original struct that was given initially.
If you provide a different value than the default for the field title
you should see that it will be tracked as a change.
Because it works this way, now if you had a record that you were trying to update instead of creating, if you cast against that record some params that were actually the same as it already had, ecto can skip trying to update the record, since it could see that nothing had changed. In the case of an insert, even if it has no changes, Ecto will attempt to insert it.