Как вставить основные и подробные данные сразу по ecto - PullRequest
1 голос
/ 23 апреля 2020

У меня есть основные данные и подробные данные. Я хотел бы вставить оба одновременно.

-------
 main
-------
 id
 name
-------

---------
 detail
---------
 id
 main_id
 name
---------

Я использую Multi транзакцию по ecto. Однако я не знаю, как вставить сразу.

main = [name: "zaku"]
details = [%{main_id: 1, name: "hoge"}, %{main_id: 1, name: "moja"},]

Multi.new()
  |> Multi.insert(:main, Main.changeset(%Main{}, main))
  |> Multi.insert(:detail, Detail.changeset(%Detail{}, details))
  |> Repo.transaction()

Часть в детали вставки не работает. Как я могу это сделать?

Ответы [ 2 ]

2 голосов
/ 23 апреля 2020

Вставка дочернего элемента не работает из-за транзакции . При попытке вставки Detail родитель еще не существует.

Обычно для вставки зависимых записей используется Ecto.build_assoc/3, или [ не рекомендуется! ] вы можете избавиться от транзакции и вставить их один за другим с двумя запросами - это будет работать.

0 голосов
/ 24 апреля 2020

Я думаю, что ваш пример здесь похож на решение для вашего другого примера , вам просто нужно закрыть несколько запросов с помощью вызова функции transaction/1:

alias Ecto.Multi
alias Ecto.Repo

user = get_user_params_from_form() # <-- or where-ever you are getting data
email = get_email_params_from_form()

Multi.new()
    |> Multi.insert(:user, User.changeset(%User{}, user))
    |> Multi.insert(
      :email,
      # Capture the id from the previous operation
      fn %{
           user: %User{
             id: user_id
           }
         } ->
        Email.changeset(%Email{user_id: user_id}, email)
      end
    )
    |> Repo.transaction()

Лично я не считаю, что с Multi очень легко работать, поэтому иногда я предпочитаю этот другой синтаксис, где вы можете передать функцию в обратный вызов Repo.transaction / 2 . В общем, это выглядит примерно так:

Repo.transaction(fn ->
  with {:ok, thing1} <- create_thing1(attrs1) do
    create_thing2(attrs2)
  else
    {:error, e} -> Repo.rollback(e)
  end
end)


def create_thing1(attrs \\ %{}) do
  %ThingOne{}
  |> ThingOne.changeset(attrs)
  |> Repo.insert()
end

def create_thing2(attrs \\ %{}) do
  %ThingTwo{}
  |> ThingTwo.changeset(attrs)
  |> Repo.insert()
end

Следует отметить, что этот шаблон можно использовать для переноса любой задачи в транзакции. Например, если "thing2" должен взаимодействовать со сторонним API, например.

...