0
votes

Edit: I guess what I'm asking is how to do: board.members[1].username = owner

I'm trying to make owner of the object (input by user) be among its members, which is a map linking a member name to its role, both being strings. This should happen as the new object is created.

Edit: my sorry attempt at making it simple

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

Also, this field :members, :map, default: %{:owner => "Manage"}

2
It would be helpful if you include some code of what you have tried. - Everett
@Everett It's too embarrassing. board = Repo.insert!(%Board{members: %{:owner => "Manage"}} in def changeset(board, attrs) do just below |> validate_required(... - Stevo Ilišković

2 Answers

1
votes

with Repo.insert you are immediatelly jumping to the database ... why not something like

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

of course in order to handle different behaviour that could/should be extracted to the function and called in the same way ... but for simple create this should do.

1
votes

Unfortunately, Ecto doesn't support something built-in which allows you to do this. Here's what you can do to accomplish it:

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

Quick tip:

Calling cast_default_members_from_owner after cast with :members allows you to use %{struct.owner => "Manage"} as a default which can be overridden in attrs, and calling it before validate_required allows you to ensure that the default value is always applied.