如何使用一个字段的转换值作为 Ecto 中另一个字段的默认值

How to use a cast value of one field as a default for another field in Ecto

编辑:我想我想问的是如何做: board.members[1].username = owner

我正在尝试使对象(用户输入)的 owner 成为其 members 中的一个,这是一个将成员名称与其角色相关联的映射,两者都是字符串。这应该在创建新对象时发生。

编辑:我很抱歉试图让它变得简单

defmodule Vision.Boards.Board do
  use Ecto.Schema
  import Ecto.Changeset

  schema "boards" do
    field :members, :map
    field :owner, :string
    field :team_name, :string
    field :title, :string

    timestamps()
  end

  @doc false
  def changeset(board, attrs) do
    board
    |> cast(attrs, [:title, :owner, :team_name])
    |> validate_required([:title, :owner, :team_name])

    board =
      Repo.insert! %Board{members: %{:owner => "Manage"}}
  end
end

另外,这个field :members, :map, default: %{:owner => "Manage"}

使用 Repo.insert 你会立即跳转到数据库......为什么不像

board
    |> cast(attrs, [:title, :owner, :team_name])
    |> validate_required([:title, :owner, :team_name])
    |> put_change(:members, %{:owner => "Manage"})

当然是为了处理不同的行为,could/should 被提取到函数中并以相同的方式调用...但是为了简单的创建,应该这样做。

不幸的是,Ecto 不支持允许您执行此操作的内置功能。您可以通过以下方式实现它:

defmodule Vision.Boards.Board do
  use Ecto.Schema
  import Ecto.Changeset
  
  # ..

  @doc false
  def changeset(board, attrs) do
    board
    |> cast(attrs, [:members, :title, :owner, :team_name])
    |> cast_default_members_from_owner()
    |> validate_required([:members, :title, :owner, :team_name])
  end

  defp cast_default_members_from_owner(changeset) do
    struct = apply_changes(changeset)

    if is_nil(struct.members) and not is_nil(struct.owner) do
      put_change(changeset, :members, %{struct.owner => "Manage"})
    else
      changeset
    end
  end
end

快速提示:

cast 之后用 :members 调用 cast_default_members_from_owner 允许您使用 %{struct.owner => "Manage"} 作为默认值,它可以在 attrs 中被覆盖,并在之前调用它validate_required 允许您确保始终应用默认值。