Skip to content

:map type in Schemaless Changeset.cast/3 only accepts single type #4271

@ozthegnp

Description

@ozthegnp

Elixir version

1.14.4

Database and Version

n/a

Ecto Versions

3.9.5

Database Adapter and Versions (postgrex, myxql, etc)

n/a

Current behavior

This works, and it means that all keys in the map must be strings.

types = %{foo: {:map, :string}}
cs = cast({%{}, types}, %{foo: %{bar: "hello", baz: "world"}}, Map.keys(types))
IO.inspect(cs.valid?)IO.inspect(cs.valid?)
# true

This also works to detect non-string fields within the nested map.

types = %{foo: {:map, :string}}
cs = cast({%{}, types}, %{foo: %{bar: 3}}, Map.keys(types))
IO.inspect(cs.valid?)
# false
IO.inspect(cs.errors)
# errors: [foo: {"is invalid", [type: {:map, :string}, validation: :cast]}]

However, when we try to define a specific type of the nested key, an error is raised.

types = %{foo: {:map, %{bar: :string}}}
Ecto.Changeset.cast({%{}, types}, %{foo: %{bar: "hello}}, Map.keys(types))
# ** (FunctionClauseError) no function clause matching in Ecto.Type.cast_fun/1

# The following arguments were given to Ecto.Type.cast_fun/1:

#     # 1
#     %{bar: :string}

It seems we are limited to uniform types exclusively.

Expected behavior

We should be able to define custom types for each key in the map. e.g:

types = %{foo: {:map, %{bar: :string, baz: :integer}}}
Ecto.Changeset.cast({%{}, types}, %{foo: %{bar: "hello", baz: 1}}, Map.keys(types))
IO.inspect(cs.valid?)
# true

Thank you

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions