使用changeset将多行插入PostgreSQL Ecto数据库时出错

时间:2017-05-24 09:09:18

标签: elixir ecto elixir-framework

我有这个功能可以将所有类别插入到特定产品的productCategory表中。

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时登录到控制台的两件事 - 产品检查和错误:

%{"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的情况但是当我在数据库中插入多行时,我不确定如何做到这一点。我做错了什么?

2 个答案:

答案 0 :(得分:1)

您可以使用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的文档中详细了解Repo.transactionEcto.Repo.transaction/2中为Ecto.Multi返回的值。

答案 1 :(得分:0)

我认为您需要Enum.map中的repo.ex,而不是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

的原因