调用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
最佳答案
按照你的说法,恐怕你必须滚动自己的代码:在提供的示例中,你正在处理子关联(用户),尝试管理它的父级(例如颜色)。这必须手动完成。
不过要添加的代码量其实并没有那么大。用户创建的代码如下所示:
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)
这当然需要 color 在其架构中声明 has_one :user, User
或 has_many :users, User
关联。
关于elixir - ectocast_assoc 查找或创建,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40580151/