【问题标题】:ecto cast_assoc find or createecto cast_assoc 查找或创建
【发布时间】:2019-07-19 22:56:14
【问题描述】:

当调用cast_assoc 时,我想要一个“查找或创建”样式的行为。在 Ecto 中是否有一种简单的方法可以做到这一点?

作为一个人为的例子,userbelongs_to 是最喜欢的color,也就是unique by 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

【问题讨论】:

  • 为什么不提供所有颜色的下拉菜单,并选择创建自定义颜色?

标签: elixir phoenix-framework ecto


【解决方案1】:

按照您的说法,恐怕您必须编写自己的代码:在提供的示例中,您正在处理子关联(用户),试图管理它的父级(例如颜色)。这必须手动完成。

要添加的代码量其实并没有那么大。创建用户的代码如下所示:

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, Userhas_many :users, User 关联。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-28
    • 2017-02-27
    • 2013-12-17
    • 2013-05-26
    • 1970-01-01
    • 2020-01-09
    相关资源
    最近更新 更多