Phoenix Ecto:未插入外键

时间:2016-08-10 15:22:02

标签: elixir phoenix-framework ecto

我正在将包含外键的模型A插入到另一个模型B中。

defmodule MyApp.ModelA do
  use MyApp.Web, :model

  schema "model_a" do
    field :type, :string, null: false
    field :data, :string, null: false
    belongs_to :model_b, MyApp.ModelB
    timestamps()
  end

  @required_fields ~w(type data)
  @optional_fields ~w()

  @doc """
  Builds a changeset based on the `struct` and `params`.
  """
  def changeset(struct, params \\ %{}) do
    struct
    |> cast(params, @required_fields, @optional_fields)
    |> assoc_constraint(:model_b)
  end
end

和模型B:

defmodule MyApp.ModelB do
  use MyApp.Web, :model

  schema "model_b" do
    field :username, :string
    field :pass, :string
    has_many :model_a, MyApp.ModelA
    timestamps()
  end

  @required_fields ~w(username pass)
  @optional_fields ~w()

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

模型B存在,因为我可以通过Repo.all(ModelB)获得它。

模型成功验证了变更集,当我打印模型A变更集结构时,我可以看到model_b_id值。

但插入时,未插入引用。虽然我在打印模型A的变更集时可以看到它,但在MySQL日志中,此字段完全缺失,不在INSERT查询中。

我玩了一点,如果我强制这个引用字段在MySQL表中不为null,那么当插入Repo.insert(...时,我得到一个{"does not exist", []}这个外键字段。)响应,尽管模型B存在于数据库中。

3 个答案:

答案 0 :(得分:0)

我不认为Ecto会从你的结构中删除ID字段 - 我一直在做这样的事情:

defmodule MyApp.ModelA do
  use MyApp.Web, :model

  schema "model_a" do
    field :type, :string, null: false
    field :data, :string, null: false
    belongs_to :model_b, MyApp.ModelB
    timestamps()
  end

  @required_fields ~w(type data model_b_id)
  @optional_fields ~w()

  @doc """
  Method head to define what's coming.
  """
  def changeset(model, params \\ :empty)

  @doc """
  Catches the case where you're passing in a model_b struct instead of an integer.
  Just makes a recursive call with the correct type.
  """
  def changeset(model, %{model_b_id: nil, agency: %MyApp.ModelB{} = model_b} = params) do
    changeset(model, %{params | model_b_id: model_b.id})
  end

  @doc """
  I normally use assoc_constraint on the table index itself like so
  """
  def changeset(struct, params \\ %{}) do
    struct
    |> cast(params, @required_fields, @optional_fields)
    |> assoc_constraint(:model_b, name: :model_as_model_b_id_fkey)
  end
end

答案 1 :(得分:0)

我对回答这个问题非常感兴趣。

我失去了> 7小时到'#34;'我知道这一行已经创建了,为什么改变集不是让我存储引用?"

我想我会给那些对检查您的数据库迁移参考感到同样沮丧的人留下建议。

我确信我已经对它们进行了三次检查,但是在那里睡了一夜之后,它就很明显了。我设置了对错误表的引用。

希望这能节省一些时间。

答案 2 :(得分:0)

对我有用的是:

model_b = Repo.get(MyApp.ModelB, 2)
model_a_changeset = Ecto.build_assoc(model_b, :model_as,
    type: "Model type",
    data: "Model data"
)
Repo.insert! model_a_changeset

(数字 2 是model_b的id示例,您必须弄清楚如何检索正确的父ID)

有时您需要在变更集上明确指定外键,如下所示:

model_a_changeset = Ecto.build_assoc(model_b, :model_as,
    type: "Model type",
    data: "Model data",
    model_b_id: model_b.id
)