【问题标题】:How to use Ecto's has_many and cast_assoc如何使用 Ecto 的 has_many 和 cast_assoc
【发布时间】:2016-10-05 12:54:43
【问题描述】:

我是 Elixir 和 Ecto 的新手,我需要一些有关 Ecto 的 has_many 和 cast_assoc 的帮助。无法理解基础知识,例如我如何使用 assoc one 创建一个新模型。

这是我的 Has_Model:

defmodule Example.Has_Model do
  use Ecto.Schema
  import Ecto.Changeset
  alias Example.Repo
  alias Example.Has_Model

  schema "has_models" do
    has_many :belong_models, Example.Belong_Model
    field :name, string
    timestamps
  end

  def changeset(has_model, params \\ %{}) do
    has_model
    |> cast(params, [:name])
  end
end

这是 Belong_Model:

defmodule Example.Belong_Model do
  use Ecto.Schema
  import Ecto.Changeset
  alias Example.Repo
  alias Example.Belong_Model

  schema "belong_models" do
    belongs_to :has_model, Example.Has_Model
    field :name, string
    timestamps
  end

  def changeset(belong_model, params \\ %{}) do
    belong_model
    |> cast(params, [:name])
    |> cast_assoc(:has_model, required: true)
  end
end

现在,我尝试做的是首先创建一个 Has_Model

iex()> changeset = Example.Has_Model.changeset(%Has_Model, %{name: "I have"})
iex()> Example.Repo.insert(changeset)

这项工作还可以。

然后我想创建一个新的 Belong_Model 并使用之前创建的 Has_Model:

iex()> has = Example.Has_Model |> Example.Repo.get_by(name: "I have")
iex()> changeset = Example.Belong_Model.changeset(%Belong_Model, %{name: "I belong", belongs_to: has})

这就是失败的地方:

** (Ecto.CastError) expected params to be a map, got: `%Example.Has_Model{__meta__: #Ecto.Schema.Metadata<:loaded, "has_models">, id: 1, inserted_at: #Ecto.DateTime<2016-10-04 19:39:38>, name: "I have", updated_at: #Ecto.DateTime<2016-10-04 19:39:38>, belong_models: #Ecto.Association.NotLoaded<association :belong_models is not loaded>}`
    (ecto) lib/ecto/changeset.ex:345: Ecto.Changeset.do_cast/4
    (example) lib/example/models/has_model.ex:15: Example.Has_Model.changeset/2
    (ecto) lib/ecto/changeset/relation.ex:99: Ecto.Changeset.Relation.do_cast/5
    (ecto) lib/ecto/changeset/relation.ex:235: Ecto.Changeset.Relation.single_change/5
    (ecto) lib/ecto/changeset.ex:571: Ecto.Changeset.cast_relation/4
    (example) lib/example/models/belong_model.ex:16: Example.Belong_Model.changeset/2

发生了什么,解决方案是什么?我一整天都在尝试改变这些参数,但我就是不明白。

【问题讨论】:

  • 尝试更改%{name: "I belong", belongs_to: has} -> %{name: "I belong", has_model_id: has.id}
  • 这会添加正确的 id,但现在我不能在 Belong_Model 变更集上使用 cast_assoc(:has_model, required: true),否则我会得到 errors: [has_model: {"can't be blank", []}]

标签: elixir ecto


【解决方案1】:

我想你要找的是build_assoc/3

iex()> has = Example.Has_Model |> Example.Repo.get_by(name: "I have")
iex()> belong_assoc = Ecto.build_assoc(has, :belong_models, has_model_id: has.id, name: "I belong")
iex()> Repo.insert!(belong_assoc)

cast_assoc/3 应该这样使用:

iex()> changeset = Example.Belong_Model.changeset(%Belong_Model, %{name: "I belong", has_model: %{name: "I have"})
iex()> Repo.insert!(changeset)

注意:

将从 changeset.params 中检索给定关联的参数,并调用关联模块中的变更集函数

【讨论】:

  • 通过 build_assoc 示例,我确实可以将关联模型包含到新模型中,但它不使用变更集进行验证。如果在 Repo.insert!(belong_assoc) 之前我做 changeset = Example.changeset(%Belong_Model{}, belong_assoc) 我再次得到 (Ecto.CastError) expected params to be a map, got... 错误。
  • 到目前为止,我发现使用现有 Has_Model 创建新 Belong_Model 的唯一可行方法是在不需要的情况下使用 has_model_id:在 cast_assoc 上为 true,如下所示:iex()&gt; changeset = Example.Belong_Model.changeset(%Belong_Model, %{name: "I belong", has_model_id: has.id})。如果我在 Belong_Model 变更集上使用 cast_assoc(:has_model, required: true),那么我会收到一条错误消息,说它不能为空,即使我使用了 has_model_id...
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多