Ecto: Casting empty strings is broken

Created on 13 Sep 2016  路  4Comments  路  Source: elixir-ecto/ecto

Environment

  • Elixir version: 1.3.0
  • Ecto version: latest@master (currently ref: 5222d528879c2671daa7230f275bed3818a51278)
  • Operating system: OS X

    Current behavior

An empty string and nil are not equivalent, however Ecto currently silently transforms empty strings into nil.

defmodule User do
  use Ecto.Schema
  import Ecto.Changeset

  schema "users" do
    field :name, :string
  end

  def update(data, params) do
    data
    |> cast(params, ~w(name))
  end
end

changeset_1 =
  %User{name: ""}
  |> User.update(%{name: ""})

IO.inspect(changeset.changes) #=> %{name: nil}

changeset_2 =
  %User{name: "Sean"}
  |> User.update(%{name: ""})

IO.inspect(changeset.changes) #=> %{name: nil}

Expected behavior

There should be no changes in changeset_1, and the result should be an empty string and not a nil value in changeset_2 above.

Most helpful comment

If anyone ends up on this page after a frustrating search, one solution to the empty-strings/null problem is to make sure your schema defines a relevant default value for each field, e.g.

field :street2, :string, size: 64, null: false, default: ""

Without the default: "" bit, you may run into trouble when casting data. HTH.

All 4 comments

@stavro Ecto 2.0 introduced something called :empty_values where some values are automatically cast to their default value. For example, if you set field :name, :string, default: "", the value should then always be cast to an empty string when an empty value is found.

@josevalim this is biting us too. It's not clear what you can do with :empty_values. I don't want to set the default to "" because I want to be able to differentiate between "" and nil after the cast. Is there any way to do that?

On Ecto master you can pass the :empty_values option on cast. On 2.1, you can change it manually. Instead of:

struct
|> cast(...)

do:

struct
|> Ecto.Change.change({})
|> Map.put(:empty_values, [])
>| cast(...)

You can always hide it a function to remove the duplication.

If anyone ends up on this page after a frustrating search, one solution to the empty-strings/null problem is to make sure your schema defines a relevant default value for each field, e.g.

field :street2, :string, size: 64, null: false, default: ""

Without the default: "" bit, you may run into trouble when casting data. HTH.

Was this page helpful?
0 / 5 - 0 ratings