Help with Ecto.Enum fields and how to pass params into the changeset

Hi,

I’m trying to use Ecto.Enum to store what days a given object is valid on. I have the following code:

defmodule Test do

  use Ecto.Schema

  import Ecto.Changeset

  schema "discount_code" do
    field(:days, Ecto.Enum, values: ~W(Monday Tuesday Wednesday Thursday Friday Saturday Sunday)a)
  end

  def create(attrs) do
    changeset(%__MODULE__{}, attrs)
  end

  def changeset(%__MODULE__{} = test, attrs) do
    test
    |> cast(attrs, [:days])
  end
end

I’ve tried various attributes to feed into the create function, including:

%{days: [:Monday :Tuesday :Wednesday]} |> Test.create()
%{days: ["Monday", "Tuesday", "Wednesday"]} |> Test.create()
%{days: "Monday, Tuesday, Wednesday"%} |> Test.create()

however when I try I get:

%{days: [:Monday]} |> Test.create()                          
#Ecto.Changeset<
  action: nil,
  changes: %{},
  errors: [
    days: {"is invalid",
     [
       type: {:parameterized, Ecto.Enum,
        %{
          on_dump: %{
            Friday: "Friday",
            Monday: "Monday",
            Saturday: "Saturday",
            Sunday: "Sunday",
            Thursday: "Thursday",
            Tuesday: "Tuesday",
            Wednesday: "Wednesday"
          },
          on_load: %{
            "Friday" => :Friday,
            "Monday" => :Monday,
            "Saturday" => :Saturday,
            "Sunday" => :Sunday,
            "Thursday" => :Thursday,
            "Tuesday" => :Tuesday,
            "Wednesday" => :Wednesday
          },
          values: [:Monday, :Tuesday, :Wednesday, :Thursday, :Friday, :Saturday,
           :Sunday]
        }},
       validation: :cast
     ]}
  ],
  data: #Test<>,
  valid?: false
>

What am I doing wrong? What format should these params be?

Thanks in advance

Ecto.Enum validates that the value is one of the available ones. The way it was intended to be used was this:

schema "discount_code" do
  field :day, Ecto.Enum, values: ~W(Monday Tuesday Wednesday Thursday Friday Saturday Sunday)a
end

%{day: :Monday} |> Test.create()
2 Likes

-.- thanks

You should be able to do the following if you need a list of values.

field :days, {:array, Ecto.Enum}, values: …
1 Like

Thanks, I was going to replace it completely but this looks like the best solution.

While I obviously didn’t know what an Enum is used for, I will say the error message could definitely have been a bit more informative.

I had the same challenge and followed the hint with the Ecto.Enum, by @LostKobrakai

The use case is managing user acl’s. Adding, and removing permissions for authorization.
This is what I came up with and works fine for me.
Maybe this helps anyone (or anyone has some hints for improvements)

The Schema: (extract)

@permissions [:any, :staff, :useradmin]

  schema "users" do
    ...
    field(:acl, {:array, Ecto.Enum}, values: @permissions, default: [:any])
    ...
    timestamps()
  end

The Changesets:

@doc """
  Changeset for adding one or more permission(s) to the users acl.
  """
  def add_permission_changeset(user, attrs) do
    user
    |> cast(attrs, [:acl])
    |> validate_required([:acl])
    |> add_permission_if_not_existent(user, attrs)
  end

  defp add_permission_if_not_existent(changeset, user, attrs) do
    add_permissions = Enum.filter(attrs.acl, fn x -> not Enum.member?(user.acl, x) end)
    changeset |> change(acl: user.acl ++ add_permissions)
  end

  @doc """
  Changeset for removing one or more permission(s) from the users acl.
  """
  def remove_permission_changeset(user, attrs) do
    user
    |> cast(attrs, [:acl])
    |> validate_required([:acl])
    |> remove_permission_if_existant(user, attrs)
  end

  defp remove_permission_if_existant(changeset, user, attrs) do
    changeset |> change(acl: Enum.filter(user.acl, fn x -> not Enum.member?(attrs.acl, x) end))
  end

And the functions for changing the data:

def add_permissions(user, attrs) do
    user
    |> User.add_permission_changeset(attrs)
    |> Repo.update()
  end

  def remove_permissions(user, attrs) do
    user
    |> User.remove_permission_changeset(attrs)
    |> Repo.update()
  end