thojanssens1
From `Repo.insert/2` to `Repo.transaction/2`
I have to convert the following code using Repo.insert/2
def create_question(attrs) do
%Question{}
|> Question.creation_changeset(attrs)
|> Repo.insert()
end
to use a transaction:
def create_question(attrs) do
question_changeset =
Question.creation_changeset(%Question{}, attrs)
Multi.new()
|> Multi.insert(:question, question_changeset)
|> Multi.run(:do_something, fn _repo, %{question: question} ->
# do something
{:ok, nil}
end)
|> Repo.transaction()
end
The problem is that now I have to change the code of my controller because the return value of Repo.transaction/2 is different than the return value of Repo.insert/2.
The controller’s action code is the following:
def create(conn, %{"question" => question_params}) do
case Quiz.create_question(question_params) do
{:ok, question} ->
conn
|> put_flash(:info, "Question created successfully.")
|> redirect(to: Routes.admin_question_path(conn, :show, question))
{:error, %Ecto.Changeset{} = changeset} ->
render(conn, "new.html", changeset: changeset)
end
end
I think it’s wrong to change controller’s code whether I use a transaction or not? I think that the controller always either needs a struct or a changeset to display the errors, just as Repo.insert/2 returns?
Any advice is welcome on how to refactor that properly, on where I have to change code.
Marked As Solved
idi527
![]()
If you don’t need the full result including :do_something, then you can return the same tagged tuple as before:
def create_question(attrs) do
question_changeset =
Question.creation_changeset(%Question{}, attrs)
Multi.new()
|> Multi.insert(:question, question_changeset)
|> Multi.run(:do_something, fn _repo, %{question: question} ->
# do something
{:ok, nil}
end)
|> Repo.transaction()
|> case do
{:ok, %{question: question}} -> {:ok, question}
{:error, :question, %Ecto.Changeset{} = changeset, _changes} -> {:error, changeset}
end
end
Also Liked
LostKobrakai
You want to change the behavior of the function, but not change the values returned? You could manually add an error (and action) to question_changeset if you really want to. Just be aware that you might encounter errors, which are not really related to the form itself and therefore don’t fit that option.
LostKobrakai
How about {:error, :upload_failed}. Changesets are very good in folding error messages for fields, which in your case might work out as well, but they don’t have means of holding error messages, which are not specific to a certain field in the form.
LostKobrakai
There’s two parts to that. The public api of the context function and whatever is calling it (the controller in your case). The API of your context should return whatever you feel is needed to describe what happened. The controllers job is then to convert the result into stuff on the rendered website, which informs the user. Error tuples is one potential result your context function could have. If a caller is supposed to handle an error you’d not want to use raise.
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance









