使用变更集将多行插入 PostgreSQL Ecto 数据库时出错
Error inserting multiple rows into a PostgreSQL Ecto database using a changeset
我有这个功能可以将所有类别插入特定产品的 productCategory table。
EG 一个产品有多个分类。
在repo.ex
def insertProductCategories(conn, product, productId) do
IO.inspect(product)
changeset = Enum.each(product["categories"], fn (productCategory) ->
Api.ProductCategory.changeset(%Api.ProductCategory{c_id: productCategory["value"], p_id: productId})
end)
errors = changeset.errors
valid = changeset.valid?
case insert(changeset) do
{:ok, product} ->
conn
|> put_resp_content_type("application/json")
|> send_resp(200, Poison.encode!(%{
successs: product
}))
{:error, changeset} ->
conn
|> put_resp_content_type("application/json")
|> send_resp(500, Poison.encode!(%{
failure: changeset
}))
end
end
productCategory.ex
defmodule Api.ProductCategory do
use Ecto.Schema
@derive {Poison.Encoder, only: [:c_id, :p_id]}
schema "productCategories" do
field :c_id, :integer
field :p_id, :integer
end
def changeset(productCategory, params \ %{}) do
productCategory
|> Ecto.Changeset.cast(params, [:c_id, :p_id])
|> Ecto.Changeset.validate_required([:c_id, :p_id])
end
end
这是当 insertProductCategories
运行时记录到控制台的 2 件事 - 产品检查和错误:
%{"brand" => "Healtheries",
"categories" => [%{"categoryId" => 1, "label" => "Meat",
"selectedAdd" => true, "selectedSearch" => false, "value" => 1},
%{"categoryId" => 1, "label" => "Dairy", "selectedAdd" => true,
"selectedSearch" => false, "value" => 2},
%{"categoryId" => 1, "label" => "Confectionary", "selectedAdd" => true,
"selectedSearch" => false, "value" => 3},
%{"categoryId" => 1, "label" => "Dessert", "selectedAdd" => true,
"selectedSearch" => false, "value" => 4},
%{"categoryId" => 1, "label" => "Baking", "selectedAdd" => true,
"selectedSearch" => false, "value" => 5},
%{"categoryId" => 1, "label" => "Condiments", "selectedAdd" => true,
"selectedSearch" => false, "value" => 6},
%{"categoryId" => 1, "label" => "Beverages", "selectedAdd" => true,
"selectedSearch" => false, "value" => 7}],
"description" => "Yummy chocolate bits for baking", "image" => "no-image",
"name" => "Chocolate Bits"}
20:49:46.103 [error] #PID<0.340.0> running Api.Router terminated
Server: 192.168.20.3:4000 (http)
Request: POST /products
** (exit) an exception was raised:
** (UndefinedFunctionError) function :ok.errors/0 is undefined (module :ok is not available)
:ok.errors()
(api) lib/api/repo.ex:33: Api.Repo.insertProductCategories/3
(api) lib/api/router.ex:1: Api.Router.plug_builder_call/2
(api) lib/plug/debugger.ex:123: Api.Router.call/2
(plug) lib/plug/adapters/cowboy/handler.ex:15: Plug.Adapters.Cowboy.Handler.upgrade/4
(cowboy) /Users/Ben/Development/Projects/vepo/api/deps/cowboy/src/cowboy_protocol.erl:442: :cowboy_protoco
l.execute/4
我只对超过 1 行的数据库插入执行过此操作,但它没有任何验证:
Enum.each(subcategories, fn (subcategory) -> insert(subcategory) end)
而且我只使用了一个带有验证的变更集来插入一行:
def insertProduct(conn, product) do
changeset = Api.Product.changeset(%Api.Product{}, product)
errors = changeset.errors
valid = changeset.valid?
case insert(changeset) do
{:ok, product} ->
conn
|> put_resp_content_type("application/json")
|> send_resp(200, Poison.encode!(%{
successs: product
}))
{:error, changeset} ->
conn
|> put_resp_content_type("application/json")
|> send_resp(500, Poison.encode!(%{
failure: changeset
}))
end
end
我正在尝试合并这些技术。我想将验证码保留在那里(例如 :ok
和 :error
的情况,但是当我向数据库中插入多行时我不确定该怎么做。我做错了什么?
您可以使用 Ecto.Multi
对一堆变更集的插入进行排序,然后 运行 它在事务中。该事务将确保如果任何插入有任何错误,其余的更改将被回滚。
multi = Enum.reduce(Enum.with_index(product["categories"]), Ecto.Multi.new, fn {productCategory, index}, multi ->
changeset = Api.ProductCategory.changeset(%Api.ProductCategory{c_id: productCategory["value"], p_id: productId})
Ecto.Multi.insert(multi, index, changeset)
end)
case Repo.transaction(multi) do
{:ok, categories} ->
# categories here is a map with the index as key and struct as value
...
{:error, failed_operation, failed_value, changes_so_far} ->
...
end
您可以在 this example in the documentation and the documentation of Ecto.Repo.transaction/2 中阅读有关 Repo.transaction
为 Ecto.Multi
返回的值的更多信息。
我认为 repo.ex
需要 Enum.map
,而不是 Enum.each
。
来自文档:
each(enumerable, fun)
each(t, (element -> any)) :: :ok
Invokes the given fun for each item in the enumerable.
Returns :ok.
这就是为什么您会看到 function :ok.errors/0 is undefined
我有这个功能可以将所有类别插入特定产品的 productCategory table。
EG 一个产品有多个分类。
在repo.ex
def insertProductCategories(conn, product, productId) do
IO.inspect(product)
changeset = Enum.each(product["categories"], fn (productCategory) ->
Api.ProductCategory.changeset(%Api.ProductCategory{c_id: productCategory["value"], p_id: productId})
end)
errors = changeset.errors
valid = changeset.valid?
case insert(changeset) do
{:ok, product} ->
conn
|> put_resp_content_type("application/json")
|> send_resp(200, Poison.encode!(%{
successs: product
}))
{:error, changeset} ->
conn
|> put_resp_content_type("application/json")
|> send_resp(500, Poison.encode!(%{
failure: changeset
}))
end
end
productCategory.ex
defmodule Api.ProductCategory do
use Ecto.Schema
@derive {Poison.Encoder, only: [:c_id, :p_id]}
schema "productCategories" do
field :c_id, :integer
field :p_id, :integer
end
def changeset(productCategory, params \ %{}) do
productCategory
|> Ecto.Changeset.cast(params, [:c_id, :p_id])
|> Ecto.Changeset.validate_required([:c_id, :p_id])
end
end
这是当 insertProductCategories
运行时记录到控制台的 2 件事 - 产品检查和错误:
%{"brand" => "Healtheries",
"categories" => [%{"categoryId" => 1, "label" => "Meat",
"selectedAdd" => true, "selectedSearch" => false, "value" => 1},
%{"categoryId" => 1, "label" => "Dairy", "selectedAdd" => true,
"selectedSearch" => false, "value" => 2},
%{"categoryId" => 1, "label" => "Confectionary", "selectedAdd" => true,
"selectedSearch" => false, "value" => 3},
%{"categoryId" => 1, "label" => "Dessert", "selectedAdd" => true,
"selectedSearch" => false, "value" => 4},
%{"categoryId" => 1, "label" => "Baking", "selectedAdd" => true,
"selectedSearch" => false, "value" => 5},
%{"categoryId" => 1, "label" => "Condiments", "selectedAdd" => true,
"selectedSearch" => false, "value" => 6},
%{"categoryId" => 1, "label" => "Beverages", "selectedAdd" => true,
"selectedSearch" => false, "value" => 7}],
"description" => "Yummy chocolate bits for baking", "image" => "no-image",
"name" => "Chocolate Bits"}
20:49:46.103 [error] #PID<0.340.0> running Api.Router terminated
Server: 192.168.20.3:4000 (http)
Request: POST /products
** (exit) an exception was raised:
** (UndefinedFunctionError) function :ok.errors/0 is undefined (module :ok is not available)
:ok.errors()
(api) lib/api/repo.ex:33: Api.Repo.insertProductCategories/3
(api) lib/api/router.ex:1: Api.Router.plug_builder_call/2
(api) lib/plug/debugger.ex:123: Api.Router.call/2
(plug) lib/plug/adapters/cowboy/handler.ex:15: Plug.Adapters.Cowboy.Handler.upgrade/4
(cowboy) /Users/Ben/Development/Projects/vepo/api/deps/cowboy/src/cowboy_protocol.erl:442: :cowboy_protoco
l.execute/4
我只对超过 1 行的数据库插入执行过此操作,但它没有任何验证:
Enum.each(subcategories, fn (subcategory) -> insert(subcategory) end)
而且我只使用了一个带有验证的变更集来插入一行:
def insertProduct(conn, product) do
changeset = Api.Product.changeset(%Api.Product{}, product)
errors = changeset.errors
valid = changeset.valid?
case insert(changeset) do
{:ok, product} ->
conn
|> put_resp_content_type("application/json")
|> send_resp(200, Poison.encode!(%{
successs: product
}))
{:error, changeset} ->
conn
|> put_resp_content_type("application/json")
|> send_resp(500, Poison.encode!(%{
failure: changeset
}))
end
end
我正在尝试合并这些技术。我想将验证码保留在那里(例如 :ok
和 :error
的情况,但是当我向数据库中插入多行时我不确定该怎么做。我做错了什么?
您可以使用 Ecto.Multi
对一堆变更集的插入进行排序,然后 运行 它在事务中。该事务将确保如果任何插入有任何错误,其余的更改将被回滚。
multi = Enum.reduce(Enum.with_index(product["categories"]), Ecto.Multi.new, fn {productCategory, index}, multi ->
changeset = Api.ProductCategory.changeset(%Api.ProductCategory{c_id: productCategory["value"], p_id: productId})
Ecto.Multi.insert(multi, index, changeset)
end)
case Repo.transaction(multi) do
{:ok, categories} ->
# categories here is a map with the index as key and struct as value
...
{:error, failed_operation, failed_value, changes_so_far} ->
...
end
您可以在 this example in the documentation and the documentation of Ecto.Repo.transaction/2 中阅读有关 Repo.transaction
为 Ecto.Multi
返回的值的更多信息。
我认为 repo.ex
需要 Enum.map
,而不是 Enum.each
。
来自文档:
each(enumerable, fun)
each(t, (element -> any)) :: :ok
Invokes the given fun for each item in the enumerable.
Returns :ok.
这就是为什么您会看到 function :ok.errors/0 is undefined