调用cast_assoc
时,我想要一个'查找或创建'样式行为。在Ecto中有一种简单的方法吗?
作为一个人为的例子,user
belongs_to
是我最喜欢的color
,unique
是color.name
。如果我创建一个已经存在喜欢的颜色的新用户,我会收到错误(name has already been taken
)。相反,我想将user.color_id
设置为预先存在的color
记录。在Ecto中是否有内置功能来执行此操作,还是我必须自己推出解决方案?
用户变更集:
def changeset(struct, params \\ %{}) do
struct
|> cast(params, [:name])
|> cast_assoc(:color)
end
测试失败:
test "create user with pre-existing color" do
Repo.insert!(%Color{name: "red"})
%User{}
|> User.changeset(%{name: "Jim", color: %{name: "red"}})
|> Repo.insert!
end
答案 0 :(得分:0)
使用它的方式,恐怕您将不得不编写自己的代码:在提供的示例中,您正在处理子assoc(用户),尝试管理其父(例如颜色)。这必须手动完成。
要添加的代码量实际上并不是那么大。用于创建用户的代码如下所示:
color = Repo.get_by(Color, name: params["color"]["name"])
if color do
%User{}
|> User.changeset(params)
|> Ecto.Changeset.put_assoc(:color, color)
else
%User{}
|> User.changeset(params)
|> Ecto.Changeset.cast_assoc(:color)
end
|> Repo.insert!
或者,您应该颠倒您的方法-如果您知道color
提前存在(我认为应该存在),则可以执行以下操作:
color = Repo.get_by(Color, name: params["color"]["name"])
color
|> build_assoc(:user)
这当然需要颜色在其架构中声明has_one :user, User
或has_many :users, User
关联。