(ArgumentError) cast/3 expects a list of atom keys, got: `"name"`

I’m not sure why I got:

** (ArgumentError) cast/3 expects a list of atom keys, got: `"name"`
    (ecto 3.3.3) lib/ecto/changeset.ex:560: Ecto.Changeset.cast_key/1
    (ecto 3.3.3) lib/ecto/changeset.ex:518: Ecto.Changeset.process_param/7
    (elixir 1.10.2) lib/enum.ex:2111: Enum."-reduce/3-lists^foldl/2-0-"/3
    (ecto 3.3.3) lib/ecto/changeset.ex:504: Ecto.Changeset.cast/6
    priv/repo/seeds.exs:21: anonymous fn/2 in :elixir_compiler_2.__FILE__/1
    priv/repo/seeds.exs:47: (file)

for the following code:

alias Pxblog.Repo
alias Pxblog.Role
alias Pxblog.User

import Ecto.Query, only: [from: 2]

find_or_create_role = fn role_name, admin ->
  case Repo.all(from(r in Role, where: r.name == ^role_name and r.admin == ^admin)) do
    [] ->
      %Role{}
      |> Role.changeset(%{name: role_name, admin: admin})
      |> Repo.insert!()
    _ ->

      IO.puts("Role: #{role_name} already exists, skipping")
  end
end

find_or_create_user = fn username, email, role ->
  case Repo.all(from(u in User, where: u.username == ^username and u.email == ^email)) do
    [] ->
      %User{}
      |> User.changeset(%{
        username: username,
        email: email,
        password: "test",
        password_confirmation: "test",
        role_id: role.id
      })
      |> Repo.insert!()
    _ ->
      IO.puts("User: #{username} already exists, skipping")
  end
end

_user_role = find_or_create_role.("User Role", false)
admin_role = find_or_create_role.("Admin Role", true)
_admin_user = find_or_create_user.("admin", "admin@test.com", admin_role)

I’m on:

Erlang/OTP 22 [erts-10.6.4] [source] [64-bit] [smp:12:12] [ds:12:12:10] [async-threads:1] [hipe]

Elixir 1.10.2 (compiled with Erlang/OTP 21)

and

{:ecto_sql, "~> 3.3"},
{:jason, "~> 1.0"},
{:myxql, "~> 0.3.0"}
defmodule Pxblog.Role do
  use Ecto.Schema
  import Ecto.Changeset

  schema "roles" do
    field(:name, :string)
    field(:admin, :boolean, default: false)

    has_many(:users, Pxblog.User)

    timestamps(type: :utc_datetime)
  end

  @required_fields ~w(name admin)
  @optional_fields ~w()

  @doc """
  Creates a changeset based on the `model` and `params`.

  If no params are provided, an invalid changeset is returned
  with no validation performed.
  """
  def changeset(model, params \\ :invalid) do
    model
    |> cast(params, @required_fields, @optional_fields)
  end
end
defmodule Pxblog.User do
  use Ecto.Schema
  import Ecto.Changeset

  schema "users" do
    field(:username, :string)
    field(:email, :string)
    field(:password_digest, :string)

    belongs_to(:role, Pxblog.Role)

    has_many(:posts, Pxblog.Post)

    timestamps(type: :utc_datetime)

    # Virtual Fields
    field(:password, :string, virtual: true)
    field(:password_confirmation, :string, virtual: true)
  end

  @doc """
  Creates a changeset based on the `model` and `params`.

  If no params are provided, an invalid changeset is returned
  with no validation performed.
  """
  def changeset(model, params \\ %{}) do
    model
    |> cast(params, [:username, :email, :password, :password_confirmation, :role_id])
    |> hash_password
    |> validate_required([:username, :email, :password, :password_confirmation, :role_id])
    |> validate_confirmation(:password, message: "does not match password!")
    |> unique_constraint(:username)
    |> unique_constraint(:email)
    |> validate_length(:password, min: 4)
  end

  defp hash_password(changeset) do
    if password = get_change(changeset, :password) do
      changeset
      |> put_change(:password_digest, Comeonin.Bcrypt.hashpwsalt(password))
    else
      changeset
    end
  end
end

Can You show your User file?

Updated the question

You miss an a at the end…

iex> ~w(name admin)
["name", "admin"]
iex> ~w(name admin)a
[:name, :admin]

#
@required_fields ~w(name admin)a

I am also not sure about that…

I would use something like this instead

model
    |> cast(params, @required_fields ++ @optional_fields)
6 Likes