Reputation: 767
This is a simple function used to insert or update some data. If user data is already in db i simply update it, otherwise i insert a new row with data. Everything works fine but i have a problem with validation. Changeset definition:
def changeset(struct, params \\ %{}) do
struct
|> cast(params, [:name, :surname, :user_id])
|> validate_required([:name, :surname, :user_id])
|> unique_constraint(:user_id)
end
validate_required is currently working only during insert and not during update.
def add_or_change(user_id, new_data) do
data_from_db = data_by_user_id (user_id)
case data_from_db do
nil ->
Data.changeset(%Data{}, new_data)
|> Repo.insert()
_ ->
Changeset.change(data_from_db, new_data)
|> Repo.update()
end
end
If i try to insert "" as :name value, i get an error (can't be blank) as expected. However if i'm updating an existing row with "" as :name value, changeset does not pass through validation and my db is updated improperly. How to force validation also on change, before Repo.update()??
Upvotes: 2
Views: 1038
Reputation: 668
Don't use this:
Changeset.change(data_from_db, new_data)
Just run the same function you were already using:
Data.changeset(data_from_db, new_data)
By the way, you can actually simplify this function a lot:
def add_or_change(user_id, new_data) do
(data_by_user_id(user_id) || %Data{})
|> Data.changeset(new_data)
|> Repo.insert_or_update()
end
Upvotes: 1
Reputation: 3280
According to the doc: Ecto.Changeset/2
is meant for internal data changes, so it bypasses validations:
The function is meant for working with data internal to the application. Because of that neither validation nor casting is performed. This means
change/2
expects the keys in the changes map or keyword to be atoms.
You should use Ecto.Changeset.cast/4
to apply the validations, and then update if it is valid.
Upvotes: 1