Updating a field using Ecto one-liner?

In Ruby, I can go:

User.find_by(email: "foobar@email.com").update(email: "hello@email.com")

How can I do something similar in Elixir?

Ecto.Changeset.change(MyApp.User |> where(email: "foobar"), email: "barbaz") |> MyApp.Repo.update

you can do:

Ecto.Changeset.change( MyApp.Repo.get_by(MyApp.User, email: "foobar@email.com"), %{email: "hello@email.com"}) |> MyApp.Repo.update()

but piping is much nicer:

MyApp.Repo.get_by(MyApp.User, email: "foobar@email.com")
|> Ecto.Changeset.change(%{email: "hello@email.com"})
|> MyApp.Repo.update()

I would not do this for production code though - I would have error handling, and explicit changeset with the needed validations etc etc.

6 Likes

Ecto has made the decision to clearly separate data from action, whereas in an OO language like Ruby they’re tied together.

Consequently, you may need an extra line or two to do certain things via Ecto that you’d do from ActiveRecord. I don’t really see this as much of a problem, and it helps make it wildly clearer when and how database access actually happens.

Normal aliasing practices will also help a bit alias MyApp.Repo.

Ultimately though I’m not really sure what your question is. You ask “how” but then you provide an example that does exactly what you want. Is your question more “what’s the most succinct way I can do this?” ?

2 Likes

I would imagine this is console/iex work - that can seem a bit tedious (at first) in iex compared to rails (imho)

one trick is to have an .iex.exs file in your project root https://hexdocs.pm/iex/IEx.html#module-the-iex-exs-file

say for this example you had the following in it:

import Ecto.Changeset
alias MyApp.Repo
alias MyApp.User

then you can do:

Repo.get_by(User, email: "foobar@email.com")
|> change(%{email: "hello@email.com"})
|> Repo.update()

and you are not that many characters away from the OO rails way… but again not for production code…

5 Likes