【问题标题】:phoenix: post a model with has_many keep throw preloading errorphoenix: post a model with has_many keep throw preloading error
【发布时间】:2017-07-06 17:48:31
【问题描述】:

我这里会展示一些相关的代码,如果你需要完整的代码,你可以在 Github 上找到:https://github.com/maple-leaf/phoenix_todo

用户模型:

defmodule PhoenixTodo.User do
  use PhoenixTodo.Web, :model

  @derive {Poison.Encoder, only: [:name, :email, :bio, :todos]}

  schema "users" do
    field :name, :string
    field :age, :integer
    field :email, :string
    field :bio, :string

    has_many :todos, PhoenixTodo.Todo

    timestamps()
  end

  @doc """
  Builds a changeset based on the `struct` and `params`.
  """
  def changeset(struct, params \\ %{}) do
    struct
    |> cast(params, [:name])
    |> validate_required([])
  end
end

tod​​o_model:

defmodule PhoenixTodo.Todo do
  use PhoenixTodo.Web, :model

  @derive {Poison.Encoder, only: [:title, :content]}

  schema "todos" do
    field :title, :string
    field :content, :string

    belongs_to :user, PhoenixTodo.User

    timestamps()
  end

  @doc """
  Builds a changeset based on the `struct` and `params`.
  """
  def changeset(struct, params \\ %{}) do
    struct
    |> cast(params, [])
    |> validate_required([])
  end
end

用户控制器:

defmodule PhoenixTodo.UserController do
    use PhoenixTodo.Web, :controller

    alias PhoenixTodo.User

    def index(conn, _params) do
        json(conn, User |> Repo.all() |> Repo.preload(:todos))
    end

    def show(conn, %{"id" => id}) do
        json(conn, User|> Repo.get(id) |> Repo.preload(:todos))
    end

    def create(conn, data) do
      user = User.changeset(%User{}, data)
      #user = User.changeset(%User{}, %{name: "xxx", todos: [%PhoenixTodo.Todo{}]})
      json conn, user
    end
end

我可以从:index:show 获取所有用户或查询特定用户,但:create 总是会引发关于association 的错误。

请求:curl -H "Content-Type: application/json" -X POST -d '{"name": "abc"}' http://localhost:4000/api/users

这样引发的错误:cannot encode association :todos from PhoenixTodo.User to JSON because the association was not loaded. Please make sure you have preloaded the association or remove it from the data to be encoded

:create时如何预加载:todos?而todos在创建用户时不是必填字段,我们可以忽略这个关联吗?

【问题讨论】:

  • 你忘了做Repo.insert吗?创建变更集不会将用户插入数据库。
  • @Dogbert 我认为这不是由Repo.insert 引起的。但是我在返回 json 之前添加了这一点,并引发了同样的错误。 :create 的代码现在看起来像 user = User.changeset(%User{}, data) Repo.insert(user) json conn user
  • 是的,如您所见,您应该将Repo.insert 返回的用户结构传递给json,而不是变更集。

标签: elixir phoenix-framework ecto


【解决方案1】:

最后,我让它工作了。

事实证明json conn user 很重要。似乎json 将调用Poison 来编码Ecto.Changeset不是Ecto.Model,它由changeset 返回。所以我们应该将Ecto.Model 传递给json。

这就是我的做法。

user = User.changeset(%User{}, data)
IO.inspect user  # Ecto.Changeset
{:ok, u1} = Repo.insert(user)
IO.inspect u1   # Model!!
json conn, u1 |> Repo.preload(:todos)  # now we can successfully preload :todos

【讨论】:

    猜你喜欢
    • 2018-07-28
    • 1970-01-01
    • 2019-01-01
    • 2018-02-27
    • 2013-02-09
    • 1970-01-01
    • 2020-08-03
    • 2022-12-26
    相关资源
    最近更新 更多