I want to generate a name to a user in changeset if it was not provided. What would be a good way to do that?
I figured I should check that the name was not provided in changes, and that the user does not already have a name. But perhaps there is a simpler way?
defmodule App.Users.User do
use Ecto.Schema
import Ecto.Changeset
schema "users" do
field :name, :string
end
def changeset(user, attrs) do
user
|> cast(attrs, [:name])
|> add_name_if_missing()
|> validate_required([:name])
end
defp add_name_if_missing(%Ecto.Changeset{changes: %{name: _}} = changeset) do
changeset
end
defp add_name_if_missing(%Ecto.Changeset{data: %App.Users.User{name: nil}} = changeset) do
changeset
|> put_change(:name, generate_new_name())
end
defp add_name_if_missing(changeset) do
changeset
end
end
Hello, I’m quite new to elixir, could you explain why there are 3 pattern options and what each of them does? I have a similar case in my code, but I’m not so deep into Ecto yet.
The logic expressed by those three pattern-matching function heads goes like this:
If we’re currently updating the name field, then we’ve already got it handled. No need to do anything extra just now.
Otherwise, if we see that the already-stored name is nil (i.e. there isn’t already a name stored for the user), we generate a name and put that as a change in the changeset.
Otherwise, the user already has a name, so again, no need to do anything.
TBH there are better ways to write this code using Ecto.Changeset APIs instead of explicit pattern-matching on Changeset internals.
My attempt (not executed, so may contain bugs):
defp add_name_if_missing(changeset) do
case get_field(changeset, :name) do
nil -> put_change(changeset, :name, generate_new_name())
_ -> changeset
end
end
Ecto.Changeset.get_field looks in both changesanddata to find the given field.
One minor difference: this code runs generate_new_name when changing name from non-nil to nil. That may be a feature
defp dynamic_default(changeset, key, value_fun) do
case get_field(changeset, key) do
nil -> put_change(changeset, key, value_fun.())
_ -> changeset
end
end