Beginner here.
I have the following changeset, where there are related fields whose values I would like to use in a validation in order to ensure valid values in the database.
defmodule MyApp.Basedata.CountryContext.Country do
use Ecto.Schema
import Ecto.Changeset
schema "countries" do
field :alpha2, :string
field :alpha3, :string
field :numeric3, :string
field :name, :string
timestamps()
end
@doc false
def changeset(country, attrs) do
country
|> cast(attrs, [:alpha2, :alpha3, :numeric3, :name])
|> validate_required([:name])
|> validate_length(:alpha2, min: 2, max: 2)
|> validate_length(:alpha3, min: 3, max: 3)
|> validate_length(:numeric3, max: 3)
|> validate_format(:alpha2, ~r/^$|[A-Z]{2}/)
|> validate_format(:alpha3, ~r/^$|[A-Z]{3}/)
|> validate_format(:numeric3, ~r/^$|[0-9]{2}/)
end
end
Here some example data:
ZW;ZWE;716;Zimbabwe
XK;XKX;0;Kosovo
;;0;Turkish Republic Northern Cyprus
Zimbabwe has all three ISO codes
Kosovo only has preliminary :alpha2 and :alpha3 codes, but currently no :numeric3 value
Republic Northern Cyprus is only recognised by Turkey, and has no ISO codes.
What I would like to implement is a validator that check a non-empty :numeric3, and if the value is non-empty, then :alpha2 and :alpha3 should also be non-empty. From what I can tell the current ecto validators all take a field as a parameter, but my use case requires me to know other fields in relation to the current fields value within the validator.
I think what I will have to do is create a new function that takes a changeset as a parameter, then use code to extract the values that I need, and either
[:aplha2, "If a numeric is not empty, the alpha2 must also be not empty"]
in the case of an error
or
[]
in the case of a successfull validation. The issue I see here is Ecto will only show me the changes
in the changeset, not the actual values of all the fields in the row. So, I will be able to trap some errors of this type, but not all permutations of changes, as if I do not modify the :numeric3 value, I will have nothing to compare the :alpha2 and :alpha3 against.
Perhaps I will just have to create a postgres constraint for this case.
Or is there another workflow I can follow in cases like this?