Reputation: 1982
Is there a built-in way in Ecto to remove trailing and leading whitespaces of fields in Ecto.Changeset
when calling changeset/2
before inserting it to the database?
At the moment, I am adding two custom functions to my schema for data filtering to improve data integrity:
defp trim_fields(changeset, fields) do
Enum.reduce(fields, changeset, &trim(&2, &1))
end
defp trim(changeset, field) do
if get_change(changeset, field) do
update_change(changeset, field, &String.trim/1)
else
changeset
end
end
The function(s) can then be piped in the changeset/2
function with, e.g.
def changeset(%Customer{} = customer, attrs) do
|> cast(attrs, [:first_name, :last_name])
|> validate_required([:first_name], [:last_name])
|> trim_fields([:first_name, :last_name])
end
Since I think this is a common use-case I was wondering if there isn't a function that already provides this functionality?
If this functionality is not yet provided in Ecto, then from the Ectos API point of view, it would be convenient to add such functions and by naming them filter_trim
and filter_...
I guess?
Upvotes: 7
Views: 2923
Reputation: 59
I would like to extend zwippie's great answer.
As the documentation points out, the value may be nil
if the field was cleared. Thus, I'd use something like this:
def changeset(%Customer{} = customer, attrs) do
customer
|> cast(attrs, [:first_name, :last_name])
|> update_change(:first_name, &trim/1)
|> update_change(:last_name, &trim/1)
|> validate_required([:first_name, :last_name])
end
defp trim(binary) when is_binary(binary), do: String.trim(binary)
defp trim(nil), do: nil
And if you have many fields you want to do the trimming on, I would consider defining an auxiliary function update_changes/3
(similar to your trim_fields/2
):
def changeset(%Customer{} = customer, attrs) do
customer
|> cast(attrs, [:first_name, :last_name])
|> update_changes([:first_name, :last_name], &trim/1)
|> validate_required([:first_name, :last_name])
end
defp update_changes(changeset, keys, function) do
Enum.reduce(keys, changeset, fn key, set -> update_change(set, key, function) end)
end
Upvotes: 2
Reputation: 1971
Why not simply use :trim
?
changeset
|> cast(attrs, [:first_name, :last_name])
|> validate_required([:first_name, :last_name], [trim: true])
Docs validate_required/3.
Upvotes: 0
Reputation: 15515
I think it's best to trim input before running validations. Also, update_change
will only perform the change if there is a change for the given key.
This leads to slightly more streamlined code:
changeset
|> cast(attrs, [:first_name, :last_name])
|> update_change(:first_name, &String.trim/1)
|> update_change(:last_name, &String.trim/1)
|> validate_required([:first_name, :last_name])
Upvotes: 24