所以我有一个属于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库进行用户身份验证。
答案 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,因此错误将覆盖任何现有字段。