Ecto - 更新记录 - 未定义的函数__changeset __ / 0

时间:2017-07-15 02:43:43

标签: elixir ecto elixir-framework

尝试使用变更集更新记录时出现此错误:

14:36:29.972 [error] #PID<0.341.0> running Api.Router terminated
Server: 192.168.20.3:4000 (http)
Request: PUT /products/?p_id=11&s_id=11
** (exit) an exception was raised:
    ** (UndefinedFunctionError) function Ecto.Query.__changeset__/0 is undefined or private
        (ecto) Ecto.Query.__changeset__()
        (ecto) lib/ecto/changeset.ex:422: Ecto.Changeset.do_cast/4
        (api) lib/api/product_shop.ex:17: Api.ProductShop.changeset/2
        (api) lib/api/router.ex:168: anonymous fn/1 in Api.Router.do_match/4
        (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

代码:

pid = conn.query_params["p_id"]
sid = conn.query_params["s_id"]
price = conn.query_params["price"]
query = ProductShop |> Ecto.Query.where(p_id: ^pid)
product_shop = query |> Ecto.Query.where(s_id: ^sid)

changeset2 = Api.ProductShop.changeset(product_shop, %{price: price})
case Api.Repo.update(changeset2) do
  {:ok, product_shop} -> 
    errors = Tuple.append(errors, "Price updated")
  {:error, changeset2} -> 
    errors = Tuple.append(errors, "Price not updated")
end

这是我要更新的ProductShop:

14:38:56.658 [debug] QUERY OK source="product_shops" db=1.7ms
SELECT p0."id", p0."s_id", p0."p_id", p0."not_in_shop_count", p0."price" FROM "product_shops" AS p0 []
[%Api.ProductShop{__meta__: #Ecto.Schema.Metadata<:loaded, "product_shops">,

  id: 11, not_in_shop_count: 0, p_id: 11, price: 5.99, s_id: 11}]

为什么我收到错误?

带有变更集的 ProductShop 文件:

defmodule Api.ProductShop do
  use Ecto.Schema
  import Ecto.Changeset
  import Api.Repo
  import Ecto.Query

  @derive {Poison.Encoder, only: [:s_id, :p_id]}
  schema "product_shops" do
    field :s_id, :integer
    field :p_id, :integer
    field :not_in_shop_count, :integer
    field :price, :float
  end

  def changeset(product_shop, params \\ %{}) do
    product_shop
    |> cast(params, [:s_id, :p_id])
    |> validate_required([:s_id, :p_id])
    |> unique_constraint(:s_id, name: :unique_product_shop)
  end

  def insert_product_shop(conn, product_id, shop_id, price) do
    changeset = Api.ProductShop.changeset(%Api.ProductShop{p_id: product_id, s_id: shop_id, not_in_shop_count: 0, price: price})
    errors = changeset.errors
    valid = changeset.valid?
    case insert(changeset) do
      {:ok, product_shop} ->
        {:ok, product_shop}
      {:error, changeset} ->
        {:error, :failure}
    end
  end

  def delete_all_from_product_shops do
    from(Api.ProductShop) |> delete_all
  end

  def get_product_shops do
    Api.ProductShop |> all
  end
end
router.ex

中的

put "/products" do
    errors = {}
    IO.inspect(conn.body_params)

    product = Api.Product |> Api.Repo.get(conn.query_params["p_id"])
    shop = Api.Shop |> Api.Repo.get(conn.query_params["s_id"])

    params = for key <- ~w(image description), value = conn.body_params[key], into: %{}, do: {key, value}
    changeset = Api.Product.changeset(product, params)
    case Api.Repo.update(changeset) do
      {:ok, product} -> 
        errors = Tuple.append(errors, "Product updated")
      {:error, changeset} -> 
        errors = Tuple.append(errors, "Product not updated")
    end

    pid = conn.query_params["p_id"]
    sid = conn.query_params["s_id"]
    price = conn.query_params["price"]
    query = ProductShop |> Ecto.Query.where(p_id: ^pid)
    product_shop = query |> Ecto.Query.where(s_id: ^sid)

    changeset2 = Api.ProductShop.changeset(product_shop, %{price: price})
    case Api.Repo.update(changeset2) do
      {:ok, product_shop} -> 
        errors = Tuple.append(errors, "Price updated")
      {:error, changeset2} -> 
        errors = Tuple.append(errors, "Price not updated")
    end

    IO.inspect(errors)

    conn
      |> put_resp_content_type("application/json")
      |> send_resp(200, Poison.encode!(%{
          successs: "success",
          errors: Tuple.to_list(errors)
      }))
  end

1 个答案:

答案 0 :(得分:1)

您在架构中编写的Ecto的changeset函数默认适用于Ecto.Schema,这意味着它适用于具有已定义架构的模块。使用cast后,它处理Ecto.Changeset结构。

您的代码尝试在变更集功能中使用Ecto.Query,即:

product_shop = query |> Ecto.Query.where(s_id: ^sid)

您应该在末尾使用Repo.one()来拥有有效的ProductShop结构,然后您可以在ProductShop.changeset函数中使用它。

另请考虑重写您想要检索此product_shop的方式。请使用Repo.get_by

Repo.get_by(ProductShop, s_id: s_id, p_id: p_id)