In my app I have a method to create a new response
. A response
has a belongs_to
relationship to both a player
and match
.
In addition player
and match
both have a belongs_to
relationship to a team
.
It looks like this:
When inserting a new response
I want to validate that the player
and match
having the player_id
and match_id
foreign keys in the changeset belong to the same team
.
Currently I'm achieving this as follows. First, define a custom validation that checks the records belonging to the foreign keys:
def validate_match_player(changeset) do
player_team =
Player
|> Repo.get(get_field(changeset, :player_id))
|> Map.get(:team_id)
match_team =
Match
|> Repo.get(get_field(changeset, :match_id))
|> Map.get(:team_id)
cond do
match_team == player_team -> changeset
true -> changeset |> add_error(:player, "does not belong to the same team as the match")
end
end
and the use the validation as part of the changeset:
def changeset(model, params \\ %{}) do
model
|> cast(params, [:player_id, :match_id, :message])
|> validate_required([:player_id, :match_id, :message])
|> foreign_key_constraint(:match_id)
|> foreign_key_constraint(:player_id)
|> validate_match_player()
|> unique_constraint(
:player,
name: :responses_player_id_match_id_unique,
message: "already has an response for this match"
)
end
This works fine but involves a couple of extra SQL queries to look up the related records in order to get their team_id
foreign keys to compare them.
Is there a nicer way to do this, perhaps using constraints, that avoids the extra queries?