Elixir 在EXTO中插入相关模型

Elixir 在EXTO中插入相关模型,elixir,phoenix-framework,ecto,Elixir,Phoenix Framework,Ecto,我正在尝试插入发票结构及其关联的发票项。我能够插入发票数据,并调用匿名函数来验证、转换和插入每个项目。由于insert/2不生成退货,如果一个项目未通过验证或插入,我如何获取该项目的发票id,同时仍然能够回滚整个交易 我已将代码放入我自己的回购协议中,如下所示: def insertassoc(params) do Repo.transaction(fn -> i = Invoice.changeset(params["data"], :create) if i.va

我正在尝试插入发票结构及其关联的发票项。我能够插入发票数据,并调用匿名函数来验证、转换和插入每个项目。由于insert/2不生成退货,如果一个项目未通过验证或插入,我如何获取该项目的发票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

没有太多的最新的例子,所以很抱歉,如果这些是noob问题;-)。有人有主意吗?我尝试将发票插入放在一个私有函数中,并使用一个case块来确定主事务是否应该回滚,但我也不知道如何从中获取发票id。

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

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

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

(公认的答案适用于2.0之前的版本,Valim在该答案的评论中提到存在
put_assoc

谢谢,这更优雅。顺便说一句,对于那些将要使用它的人来说,我是如何处理错误以将它们编码成JSON的:
errors=Enum.map(items,&(&1.errors))|>Enum.into([invoice.errors])|>Enum.map(&(Enum.into(&1,%{})){:errors,errors}
API现在已经更改,因此
Repo.insert
应该替换为
Repo.insert
返回模型或在事务中引发错误。除此之外,Ecto现在还支持
put_assoc
,它允许您更改父模式及其子模式,这将使上面的代码更清晰@JoséValim-请您详细说明put_assoc将如何简化代码?我试图同时插入一个模型及其关联。模型A有许多模型B。我在考虑为模型A和模型B创建变更集,并在put_assoc中提供模型A的变更集。不。没用。例如-csModelA=%ModelA{}}}>change}>ModelA.create_变更集(params)和csModelB=%ModelB{}}>change}>put_assoc(:ModelA,csModelA)|>ModelB.create_变更集。当添加模型B抱怨外键不存在时,会出现这种情况
%My.Invoice{}
|> Ecto.Changeset.change
|> Ecto.Changeset.put_assoc(:invoice_items, [My.InvoiceItem.changeset(%My.InvoiceItem{}, %{description: "bleh"})])
|> My.Repo.insert!