Search code examples
elixirphoenix-frameworkecto

Applying Ecto validate_length on multiple fields in Changeset


Ecto 3.2.5, Phoenix 1.4.11, Elixir 1.9.4

I have an Ecto Changeset that looks something like this. I have numerous fields that I would like to run validate_length on all with the same max length.

my_model
|> validate_required(required_fields())
|> validate_length(:field, max: 255)
|> validate_length(:another_field, max: 255)
# More of the same validate_length

Instead of listing one-by-one I also tried something along these lines of this with my Changeset after the piping above, without luck.

Enum.map([:field, :another_field], fn string_field ->
  Ecto.Changeset.validate_length(ch, string_field, max: 255)
end)

validate_length only takes a single field by default, so how can I validate length on multiple fields without a line-by-line validate_length?


Solution

  • Enum.map/2 would not work because what you actually are to perform would be to reduce changeset on many fields via validation.

    Simply declare the normal private function like the below

    @spec validate_length_many(
      changeset :: Ecto.Changeset.t(), fields :: [atom()]
    ) :: Ecto.Changeset.t()
    defp validate_length_many(
      %Ecto.Changeset{} = changeset, fields
    ) when is_list(fields) do
      Enum.reduce(fields, changeset, fn field, changeset ->
        validate_length(changeset, field, max: 255)
      end)
    end
    

    and use it as

    my_model
    |> validate_required(required_fields())
    |> validate_length_many(~w[f1 f2]a)
    

    or call Enum.reduce/3 directly (it won’t pipe well because the first argument in the call to Enum.reduce/3 would be a list of fields).

    changeset = validate_required(my_model, required_fields())
    Enum.reduce([:f1, :f2], changeset, fn field, changeset ->
      validate_length(changeset, field, max: 255)
    end) #⇒ changeset