On_conflict: :nothing

I’m trying to use the new on_conflict: :nothing feature (with Postgres 9.6.1).

However, when there is a conflict, the returned struct does not get an id field, so using put_assoc fails.

category = Repo.insert!(category_changeset, on_conflict: :nothing) put_assoc(changeset, :category, category)

** (Ecto.NoPrimaryKeyValueError) struct ... is missing primary key value

1 Like

If you pass on_conflict: :nothing, it will literally do nothing and return the category as is. One trick is to try to change a fixed value, that you know it won’t change. For example, if categories have unique name and that’s the column you are checking, you can do this:

 Repo.insert!(category_changeset, on_conflict: [set: [name: category.name]])

This way you force an update and get an ID back.

PS: If you are using on_conflict: :nothing based on the Ecto book, the Ecto book has also been fixed to mention exactly this. :slight_smile:

5 Likes

Oh, when you give PostgreSQL a list of updates to perform, it also expects the :conflict_target option, which is the column (or a list of columns) we are expecting the conflict to happen:

 Repo.insert!(category_changeset, on_conflict: [set: [name: category.name]], conflict_target: :name)
1 Like

Thanks! You might want to update the Plataformatec Blog post too :slight_smile:

Using this [set: [name: category.name]] syntax, will this actually perform a write to the database even though the content is identical?

1 Like

@maxs I have no idea what it actually does, I would imagine databases would optimize it but no guarantees. When benchmarking I could see no time difference.

And good call about the blog post. :slight_smile: