Creating associated records

I have an application that allows users to create and play quizzes. Each question in the quiz has four answers, stored in separate tables. Each question also has a correct_answer field which references a particular answer. In my channel, I create a question from the passed parameters, and then use Enum.map to create four answers, using another map to get the ids, and then updating the question with the first of these ids as the correct answer. The code is as follows:

def handle_in("questions:create", %{"question" => %{"body" => body}}, socket) do
  question_params = %{quiz_id: socket.assigns.quiz_id, body: body}

  question =
    %Question{}
    |> Question.changeset(question_params)
    |> Repo.insert!

  answer =
    Enum.map(1..4, fn x ->
      %Answer{}
      |> Answer.changeset(%{body: "Answer #{x}", question_id: question.id})
      |> Repo.insert!
    end)
    |> Enum.map(&(&1.id))
    |> List.first

  changeset = Question.changeset(question, %{correct_answer: answer})

  case Repo.update(changeset) do
    {:ok, question} ->
      question = Repo.preload(question, :answers)
      view = QuestionView.render("show.json", %{question: question})

      {:reply, {:ok, view}, socket}

    {:error, _changeset} ->
      {:reply, {:error, %{error: "Error creating question"}}, socket}
  end
end

This works, but it doesn’t seem very efficient - there’s a lot of going back and forth and updating. Is there a better way of doing it?

1 Like

I can’t answer your question about efficiency right now but an obvious concern is that you should probably wrap multiple inserts into a transaction or you could end up with a question saved but not all answers.
Have you looked at Ecto.Multi? (https://hexdocs.pm/ecto/2.0.0-rc.5/Ecto.Multi.html https://hexdocs.pm/ecto/2.0.0-rc.5/Ecto.Multi.html)

Andrew

2 Likes

I’m still using Ecto v1.1.7, so haven’t come across Ecto.Multi - but it sounds like exactly what I’m looking for, so I’ll check it out. Thank you!

1 Like