What is the simplest way to do upsert with Ecto (MySQL)

In my case insert_or_update raised an error due to the unique index constraint 🤔

What did work for me was Postgres v9.5 upsert through on_conflict parameter:

(considering unique column is called user_id)

changeset
|> MyRepo.insert(
    on_conflict: :replace_all,
    conflict_target: :user_id
)

You can use Ecto.Repo.insert_or_update/2, please note that for this to work, you will have to load existing models from the database.

 model = %Post{id: 'existing_id', ...}
 MyRepo.insert_or_update changeset
 # => {:error, "id already exists"}

Example:

result =
  case MyRepo.get(Post, id) do
    nil  -> %Post{id: id} # Post not found, we build one
    post -> post          # Post exists, using it
  end
  |> Post.changeset(changes)
  |> MyRepo.insert_or_update

case result do
  {:ok, model}        -> # Inserted or updated with success
  {:error, changeset} -> # Something went wrong
end