当ecto变更集插入失败时,为什么我会丢失子关联数据?

时间:2018-01-25 17:37:37

标签: elixir phoenix-framework ecto

所以我有一个属于User模式的Client模式,并且有一个Company模式(可选)。

以下是我在注册表单中使用的客户变更集:

  @doc false
  def create_changeset(%Client{} = client, attrs) do
    client
    |> cast(attrs, [:qualification, :phone])
    |> cast_assoc(:user, with: &User.create_changeset/2)
    |> cast_assoc(:company)
    |> validate_required([:qualification])
    |> unique_constraint(:user_id)
  end

现在这里有一个Context函数,可以同时创建一个User,Client和Company。

def create_client(attrs \\ %{}) do
  %Client{}
  |> Client.create_changeset(attrs)
  |> Repo.insert()
end

当所有必需的数据及其验证在注册表单提交时都可以,插入成功,一切都按照我的希望进行。即使在插入尝试之前表单验证失败,也会返回包含所有错误的注册表单并填充先前发送的数据...

但是当用户数据库约束(例如已经采用的电子邮件)上的插入失败时,表单将呈现为仅包含用户和客户端架构的所有数据和错误。所有公司数据都将丢失。这将迫使客户重新输入他的所有公司信息。 这种行为是不可避免的吗?

如果它有用,我正在使用Phauxth库进行用户身份验证。

2 个答案:

答案 0 :(得分:0)

如果您想独立于客户创建公司,请考虑使用alternatives for cast_assoc

我不确切知道您的用例,但如果客户的数据很好并且无论用户数据如何,您似乎都希望创建公司。也许您可以明确地为每个模式调用每个changeset,而无需在Client.changeset中将其强制转换。

此外,您的客户无需再次向您发送attr,因为您已经拥有它们 - 即使Repo.insert失败,您也可以访问changeset.params,因此您可以使用它们带来一些额外的逻辑。

答案 1 :(得分:0)

您可以在执行数据库操作之前将变更集错误与变更集合并,而不是手动添加每个参数:

def create(conn, %{"client" => client}) do
  changes = Client.changeset(%Client{}, client)
  case Repo.insert(changes) do
    {:ok, _} -> ...
    {:error, errors} ->
      changes = Ecto.Changeset.merge(changes, errors)
      render(conn, "new.html", changes: changes)
  end
end

请注意,变更集的顺序很重要。第二个变更集优先于第一个1,因此错误将覆盖任何现有字段。


1 https://hexdocs.pm/ecto/Ecto.Changeset.html#merge/2