在Ecto中插入关联的模型

Dan*_*_es 17 elixir ecto phoenix-framework

我正在尝试插入发票结构及其关联的发票项目.我可以插入发票数据,并调用匿名函数来验证,转换和插入每个项目.由于insert/2不会产生返回,如果一个项目未通过验证或插入,如何在仍然能够回滚整个事务的同时获取项目的invoice_id?

我把代码放在我自己的回购中,这里是:

def insertassoc(params) do
 Repo.transaction(fn ->    
  i = Invoice.changeset(params["data"], :create)
    if i.valid? do
      Repo.insert(i)
    else
      Repo.rollback(i.errors)
    end

  insert_include = fn k ->
    c = InvoiceItem.changeset(k, :create)
    if c.valid? do
      Repo.insert(c)
    else
      Repo.rollback(c.errors)
    end
  end

  for include <- params["includes"] do
    insert_include.(Map.merge(include, %{"invoice_id" => ????}))
  end

 end)
end
Run Code Online (Sandbox Code Playgroud)

以下是我在控制器中使用它的方法:

def create(conn, params) do
 case InvoiceRepo.insertassoc(params) do
  {:ok, x} ->
    json conn, Map.merge(params, %{"message" => "OK"})
  {:error, x} ->
    json conn |> put_status(400), Map.merge(params, %{"message" 
    => "Error"})
 end
end
Run Code Online (Sandbox Code Playgroud)

有没有很多最新的例子与Ecto,所以抱歉,如果这些是noob问题;-).有人有想法吗?我尝试将发票插入放在私有函数中,并使用case块来确定主事务是否应回滚,但我无法弄清楚如何从中获取发票ID.

Jos*_*lim 28

Repo.insert/1实际上返回刚刚插入的模型.您还希望尽可能地将验证与事务处理分离.我建议如下:

invoice = Invoice.changeset(params["data"], :create)
items   = Enum.map(params["includes"], &InvoiceItem.changeset(&1, :create))

if invoice.valid? && Enum.all?(items, & &1.valid?) do
  Repo.transaction fn ->
    invoice = Repo.insert(invoice)
    Enum.map(items, fn item ->
      item = Ecto.Changeset.change(item, invoice_id: invoice.id)
      Repo.insert(item)
    end)
  end
else
  # handle errors
end
Run Code Online (Sandbox Code Playgroud)

  • 最重要的是,Ecto现在还支持`put_assoc`,它允许你改变父模式及其子模式,这将使上面的代码更清晰! (3认同)
  • API现在已更改,因此`Repo.insert`应替换为`Repo.insert!`以返回模型或在事务内引发错误. (2认同)

edm*_*dmz 8

在Ecto 2.0中,您将执行以下操作:

%My.Invoice{}
|> Ecto.Changeset.change
|> Ecto.Changeset.put_assoc(:invoice_items, [My.InvoiceItem.changeset(%My.InvoiceItem{}, %{description: "bleh"})])
|> My.Repo.insert!
Run Code Online (Sandbox Code Playgroud)

(接受的答案在2.0之前也有效,Valim在评论中提到了存在的答案put_assoc)