On_conflict: :nothing returning an id (unexpected)

Hi yall,
I’m yet again doing the tutorial from the book Phoenix 1.4 and ran into a problem, where I ran this

iex(2)> Rumbl.Repo.insert! %Rumbl.Multimedia.Category{name: "hello"},
...(2)> on_conflict: :nothing

I got the return:

%Rumbl.Multimedia.Category{
  __meta__: #Ecto.Schema.Metadata<:loaded, "categories">,
  id: 3,
  name: "hello",
  inserted_at: ~U[2025-01-20 08:46:21Z],
  updated_at: ~U[2025-01-20 08:46:21Z]
}

All are expected except for the id: 3, in the book, it is supposed to be nil. Because that’s the point of that lesson, where we are trying to “upsert” a data. But instead, it seems like I have “inserted”? the data.
Anyone knows what I’m talking about?
If more info is needed do let me know, thanks.

EDIT:
If I ran this

Rumbl.Repo.insert! %Rumbl.Multimedia.Category{name: "Test"}

Ecto will raise an error on conflict

 * "categories_name_index" (unique_constraint)

What is the actual issue?

It’s not possible to insert a entry with name "hello" twice (if your unique_constraint was defined correctly), as the constraint is checked by the database.

Double-check your setup and constraints, also make sure that you drop the database (done automatically with mix ecto.reset) if you are editing migrations, as ecto cannot track modifications to migrations that already ran.

what’ the book you’re referencing?

Books have errors, it may have one.

I think the correct behavior is what you’re seeing. The returned Category struct will have id value no matter if it was inserted, ignored or updated.

Why would you want not have an id filled in?

This is also referenced in the official ecto documentation: Constraints and Upserts

While the above won’t raise an error in case of conflicts, it also won’t update the struct given, so it will return a tag without ID.

I know this for a fact because for postgres you have to update a field on upserts to actually get the existing entry id.

2 Likes

Huh. ok didn’t know that.

Maybe the problem @Bedtimestory9 is facing is due to absent :conflict_target ?

Hi Hubert
Thanks for the reply
The book I’m referencing is "Programming Phoenix 1.4 " from Pragmatic.
The part I’m referencing from is this part:

"
Ecto allows us to do exactly that via the :on_conflict option:

iex> Rumbl.Repo.insert! %Rumbl.Multimedia.Category{name: "hello"},
...>
on_conflict: :nothing
%Rumbl.Multimedia.Category{
__meta__: #Ecto.Schema.Metadata<:loaded, "categories">,
id: nil,
inserted_at: ~N[2019-05-19 13:06:22],
name: "hello",
updated_at: ~N[2019-05-19 13:06:22]
}

The default value for :on_conflict is :raise. Once we change it to :nothing, no exceptions
are raised and you can see the returned category has a nil id, indicating that
indeed the category was not inserted.
"

1 Like

That’s fine @D4no0 pointed to the right place to look.
I’ll have a look, ty all.