For example if you were to set latitude and longitude,
you wouldn’t one to be set without the other one, is there some neat way to do this?
Using validations, either from ecto
or exvalibur
or perhaps any other validation library, or smart constructors, or providing a function that accepts only long/lat pair as arguments or whatever you can think of…
There are many ways to do this.
in ecto/postgres:
https://hexdocs.pm/ecto/Ecto.Changeset.html#check_constraint/3
https://hexdocs.pm/ecto_sql/Ecto.Migration.html#constraint/3
create constraint(“locations”, :must_have_both_lat_lon_or_none, check: “(LAT IS NULL AND LON IS NOT NULL) OR
(LAT IS NOT NULL AND LON IS NULL)”)
not totally sure of check: syntax but above should be good…
this approach is nice, a small tweak to the check:
"(latitude IS NULL AND longitude IS NULL) OR (latitude IS NOT NULL AND longitude IS NOT NULL)"
well actually after I posted I think this is so simple and overkill to put in the DB… (and having to hit the db to get the error…)
so maybe just a custom validate function on the changeset - using some pattern matching of course
def changeset(%Location{} = location, attrs) do
location
|> cast(attrs, [:lat, :lon])
|> validate_both_lat_lon_or_none
end
defp validate_both_lat_lon_or_none(changeset) do
lat = get_field(changeset, :lat)
lon = get_field(changeset, :lon)
validate_both_lat_lon_or_none(changeset, lat, lon)
end
defp validate_both_lat_lon_or_none(changeset, lat, lon) when lat == nil and lon != nil do
add_error(changeset, :lat, "lat missing")
end
defp validate_both_lat_lon_or_none(changeset, lat, lon) when lat != nil and lon == nil do
add_error(changeset, :lon, "lon missing")
end
defp validate_both_lat_lon_or_none(changeset, _, _), do: changeset
untested code…
yeah, i was thinking something like this as my first approach, so i just need to decide between the 2
Do both, having it as a constraint in the DB means the data will be known for sure coherent, and if you have a check on the changeset too then it keeps from hitting the DB. Though I’d doubt it would be hit often enough to matter so I’d just let the DB handle it.