Ecto - 更新记录 - 未定义函数 __changeset__/0

Ecto - Update a Record - undefined function __changeset__/0

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

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

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)