Ecto.Schema.Metadata help

How do I see :state for a given changeset from Ecto.Schema.Metadata to know if a changeset is going to update or insert?

#Ecto.Changeset<
action: nil,
changes: %{
contents: [
#Ecto.Changeset<action: :update, changes: %{}, errors: [],
data: #ConstraintTest.Schema.Content<>, valid?: true>,
#Ecto.Changeset<
action: :update,
changes: %{},
errors: [id: {“has already been taken”, []}],
data: #ConstraintTest.Schema.Content<>,
valid?: false
>
]
},
errors: [],
data: #ConstraintTest.Schema.Post<>,
valid?: false

There is ‘action’.

1 Like

right, so my action is nil, but it still seems to know whether to insert or update the given changeset.

I can run Repo.insert_or_update with the changeset and see whether or not it did an insert or update based on the [debug] message. so i guess my question is answered, but still curious if you can see what its going to do before you Repo.insert_or_update.

There is no way to always know if the data will be inserted or updated, as this operation is an upsert in most cases, the case when you can know is when you explicitly have the schema from a select and create a changeset with updated fields.

1 Like

According to the Ecto.Repo docs, insert_or_update/2 requires loading the existing structs from the database in order to use it. So if you follow the pattern outlined in the docs and re-posted below, the result of the pattern matching in the case block will indicate whether it’ll be inserted or updated.

Please note that for this to work, you will have to load existing structs from the database.

result =
  case MyRepo.get(Post, id) do
    nil  -> %Post{id: id} # Post not found, we build one (aka to be inserted)
    post -> post          # Post exists, let's use it (aka to be updated)
  end
  |> Post.changeset(changes)
  |> MyRepo.insert_or_update

This makes sense because there’s no way to know if the upsert will be an update or insert until you hit the database at least once to see if the id is already present or not.

1 Like