How to validate required in nested map

Hello everyone!

I’m working with some validation when creating an object. I receive some attributes and I’m using them to create a location . Attributes is something like:

attrs = %{latitude: 123.2, longitude: 123.4, metadata: %{timestamp: "2015-05-05T01:31:54", accuracy: 1}}

To create I use:

location
|> cast(attrs, [:latitude, :longitude, :metadata])
|> validate_required([:latitude, :longitude])

Is there a way I can validate required for metadata.timestamp?
Sorry if something is wrong there, I started working with Elixir this week.
Thanks!

Hello and welcome to the forum.

It looks your metadata field is a map field, or jsonb.

You might have some solutions, each with their own dis/advantages.

On simple solution could be to have a custom validation.

Another one would be to embed metadata. The document (2015) has been updated in 2019…

2 Likes

An example of custom validation (not tested)

  defp validate_field_contains_timestamp(changeset, field) when is_atom(field) do
    validate_change(changeset, field, fn
      (current_field, value) when is_map(value) ->
        case Map.fetch(value, :timestamp) do
          {:ok, _} -> []
          :error -> [{current_field, "This field must have a timestamp"}]
        end
      (current_field, _) -> [{current_field, "This field must be a map"}]
    end)
  end

Then

location
|> cast(attrs, [:latitude, :longitude, :metadata])
|> validate_field_contains_timestamp(:metadata)
|> validate_required([:latitude, :longitude])
5 Likes

Thank you @kokolegorille! Your answer helped me validating it. It worked!