Hi everyone,
I’ve slowly worked through the many issues I’ve encountered with embedded schemas and I’m finally building some familiarity and comfort with it.
However, I’ve encountered an issue that I’m stumped on, and would love your help if you have the time to take a look.
I have an embedded schema, let’s call it GameState
, whose fields I’m partially updating. I’m specifically updating one of the existing Player
s in the :players
field and adding a new Attack
to the :attacks
field. However, certain fields :defends
, :deck
, and :discard
that I’m not attempting to change or update are producing errors and invalidating my Changeset.
Why is this happening?
The parameters passed to the offending call to changeset
are below:
attrs
%{
players: [
#Ecto.Changeset<
action: nil,
changes: %{
hand: [
#Ecto.Changeset<action: :replace, changes: %{}, errors: [],
data: #MyGame.Game.Card<>, valid?: true>,
#Ecto.Changeset<action: :update, changes: %{}, errors: [],
data: #MyGame.Game.Card<>, valid?: true>,
#Ecto.Changeset<action: :update, changes: %{}, errors: [],
data: #MyGame.Game.Card<>, valid?: true>,
#Ecto.Changeset<action: :update, changes: %{}, errors: [],
data: #MyGame.Game.Card<>, valid?: true>,
#Ecto.Changeset<action: :update, changes: %{}, errors: [],
data: #MyGame.Game.Card<>, valid?: true>,
#Ecto.Changeset<action: :update, changes: %{}, errors: [],
data: #MyGame.Game.Card<>, valid?: true>,
#Ecto.Changeset<action: :update, changes: %{}, errors: [],
data: #MyGame.Game.Card<>, valid?: true>
]
},
errors: [],
data: #MyGame.Game.Player<>,
valid?: true
>,
%MyGame.Game.Player{
id: "1c75c56f-cce7-4213-a33e-bb80ba40da76",
name: "otherplayer",
hand: [
%MyGame.Game.Card{
id: "040e6f06-b79e-4787-af97-e3b90019b8fd",
suit: :club,
rank: :jack
},
%MyGame.Game.Card{
id: "12815acf-63e2-4037-851a-80fc742a8934",
suit: :spade,
rank: :queen
},
%MyGame.Game.Card{
id: "1ec13e46-3556-495e-a8d7-6e62fa85e207",
suit: :spade,
rank: :ace
},
%MyGame.Game.Card{
id: "4e0a9126-1658-48d2-a022-f79ad5743cf9",
suit: :club,
rank: :three
},
%MyGame.Game.Card{
id: "9e6393ee-cca7-49d7-94ba-ba6992a6bc23",
suit: :heart,
rank: :six
},
%MyGame.Game.Card{
id: "21c29731-3bb1-46d8-a2da-9b4ece6ad4d2",
suit: :heart,
rank: :two
},
%MyGame.Game.Card{
id: "326268a1-910a-4de1-bc72-0f5c4a4d3a81",
suit: :club,
rank: :king
}
]
}
],
attacks: [
#Ecto.Changeset<
action: nil,
changes: %{
id: "56167751-6b44-426e-b4a4-c5c5b59568e9",
card: #Ecto.Changeset<action: :insert, changes: %{}, errors: [],
data: #MyGame.Game.Card<>, valid?: true>,
attacker_index: 0,
defender_index: 1
},
errors: [],
data: #MyGame.Game.Turn.Attack<>,
valid?: true
>
]
}
However, changeset
produces the following invalid Changeset:
Invalid Changeset Result
#Ecto.Changeset<
action: nil,
changes: %{
trump_card: nil,
players: [
#Ecto.Changeset<
action: :update,
changes: %{
hand: [
#Ecto.Changeset<action: :replace, changes: %{}, errors: [],
data: #MyGame.Game.Card<>, valid?: true>,
#Ecto.Changeset<action: :update, changes: %{}, errors: [],
data: #MyGame.Game.Card<>, valid?: true>,
#Ecto.Changeset<action: :update, changes: %{}, errors: [],
data: #MyGame.Game.Card<>, valid?: true>,
#Ecto.Changeset<action: :update, changes: %{}, errors: [],
data: #MyGame.Game.Card<>, valid?: true>,
#Ecto.Changeset<action: :update, changes: %{}, errors: [],
data: #MyGame.Game.Card<>, valid?: true>,
#Ecto.Changeset<action: :update, changes: %{}, errors: [],
data: #MyGame.Game.Card<>, valid?: true>,
#Ecto.Changeset<action: :update, changes: %{}, errors: [],
data: #MyGame.Game.Card<>, valid?: true>
]
},
errors: [],
data: #MyGame.Game.Player<>,
valid?: true
>,
#Ecto.Changeset<action: :update, changes: %{}, errors: [],
data: #MyGame.Game.Player<>, valid?: true>
],
attacks: [
#Ecto.Changeset<
action: :insert,
changes: %{
id: "d484401e-7a95-45db-b1b6-ec9682d74d4c",
card: #Ecto.Changeset<action: :insert, changes: %{}, errors: [],
data: #MyGame.Game.Card<>, valid?: true>,
attacker_index: 0,
defender_index: 1
},
errors: [],
data: #MyGame.Game.Turn.Attack<>,
valid?: true
>
]
},
errors: [
defends: {"is invalid", [type: {:array, :map}]},
discard: {"is invalid", [type: {:array, :map}]},
deck: {"is invalid", [type: {:array, :map}]}
],
data: #MyGame.Game.GameState<>,
valid?: false
>
The following is the code for the embedded schema:
Embedded Schema
@primary_key {:id, :binary_id, autogenerate: false}
embedded_schema do
embeds_many :deck, Card, on_replace: :delete
embeds_many :discard, Card, on_replace: :delete
embeds_one :trump_card, Card, on_replace: :delete
embeds_many :players, Player, on_replace: :delete
field :current_player_index, :integer, default: 0
field :current_turn, Ecto.Enum,
values: [:init_attack, :init_defend, :open_season],
default: :init_attack
embeds_many :attacks, Attack, on_replace: :delete
embeds_many :defends, Defend, on_replace: :delete
end
@doc false
def changeset(%__MODULE__{} = game_state, attrs) do
game_state
|> cast(attrs, [:id, :current_player_index, :current_turn])
|> put_embed(:players, attrs[:players])
|> put_embed(:deck, attrs[:deck])
|> put_embed(:discard, attrs[:discard])
|> put_embed(:trump_card, attrs[:trump_card])
|> put_embed(:attacks, attrs[:attacks])
|> put_embed(:defends, attrs[:defends])
|> validate_required([:id, :current_player_index, :current_turn])
|> validate_players
end