Skip to content

Instantly share code, notes, and snippets.

@turboza
Last active February 5, 2019 07:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save turboza/b85f1b5ed339763d2cfd050b6460b549 to your computer and use it in GitHub Desktop.
Save turboza/b85f1b5ed339763d2cfd050b6460b549 to your computer and use it in GitHub Desktop.
A confusing moment for Ecto when the data in the database is not updated when doing `Repo.update()`
defmodule Experiment do
alias SampleApp.{Repo, Order}
def run do
order = Repo.get(Order, 123)
order.status # => "new"
# Put status as "pending" while doing some action
{:ok, _} =
order
|> Order.changeset(%{status: "pending"})
|> Repo.update()
# revert to original status on failure
{:ok, _} =
order
|> Order.changeset(%{status: order.status})
|> Repo.update()
latest_order = Repo.get(Order, 123)
latest_order.status # => "pending" ??????? Why not "new"?
end
end
@turboza
Copy link
Author

turboza commented Feb 5, 2019

Root cause

Ecto will only send changes inside Changeset for database action. Which in this case, there is no change in the second update. So no database update on the second Repo.update()

# pry inside code to check

pry(1)> order |> Order.changeset(%{status: "pending"})
#Ecto.Changeset<
  action: nil,
  changes: %{status: "pending"},
  errors: [],
  data: #SampleApp.Order<>,
  valid?: true
>

pry(2)> order |> Order.changeset(%{status: order.status})
#Ecto.Changeset<
  action: nil, 
  changes: %{}, 
  errors: [],
  data: #SampleApp.Order<>, 
  valid?: true
>

Solution

Always use the latest data to send to Ecto Changeset for updating

    {:ok, updated_order} =
      order
      |> Order.changeset(%{status: "completed"})
      |> Repo.update()

    # revert to original status on failure
    {:ok, updated_order} =
      updated_order
      |> Order.changeset(%{status: order.status})
      |> Repo.update()

    latest_order = Repo.get(Order, 123)
    latest_order.status # => "new" -- It's correct!

Thanks to info from the issue: elixir-ecto/ecto#1798

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment