在 Ecto 中插入关联模型
Inserting associated models in Ecto
我正在尝试插入一个发票结构及其关联的发票项目。我能够插入发票数据,并调用匿名函数来验证、转换和插入每个项目。由于 insert/2 不会生成 return,如果一个项目验证或插入失败,我如何才能获得项目的 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
下面是我如何在我的控制器中使用它:
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
Ecto 的最新示例并不多,如果这些是菜鸟问题,我们深表歉意 ;-)。有人有想法吗?我尝试将 invoice insert 放入私有函数中,并使用 case 块来确定主事务是否应该回滚,但我也不知道如何从中取回 invoice id。
Repo.insert/1
实际上是 returns 您刚刚插入的模型。您还希望尽可能将验证与事务处理分离。我建议如下:
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
的存在)
我正在尝试插入一个发票结构及其关联的发票项目。我能够插入发票数据,并调用匿名函数来验证、转换和插入每个项目。由于 insert/2 不会生成 return,如果一个项目验证或插入失败,我如何才能获得项目的 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
下面是我如何在我的控制器中使用它:
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
Ecto 的最新示例并不多,如果这些是菜鸟问题,我们深表歉意 ;-)。有人有想法吗?我尝试将 invoice insert 放入私有函数中,并使用 case 块来确定主事务是否应该回滚,但我也不知道如何从中取回 invoice id。
Repo.insert/1
实际上是 returns 您刚刚插入的模型。您还希望尽可能将验证与事务处理分离。我建议如下:
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
的存在)