I have an ecto model for an address with this (simplified) schema:
defmodule Address do
use Ecto.Model
schema "addresses" do
field :zip, :string
field :country, :string
# snip
end
@countries_requiring_zip ~w(US) # snip
def changeset(model, params \\ :empty) do
model
|> cast(params, ~w(country), ~w(zip))
|> validate_zip
end
defp validate_zip(changeset) do
if get_field(changeset, :country) in @countries_requiring_zip do
# ????
end
changeset
end
end
I want to mark zip
as required instead of optional, but only if the country is in a whitelist, but I can't figure out a clean way to write the validation. How do I add that constraint?
Conditionally required fields must be completed if certain conditions are met. For example if an institution responds “Yes” that is has a certain program, then a description of that program may be required. (The trigger for each conditionally required field is outlined in the STARS Technical Manual).
You can simply define multiple casts too:
def changeset(model, params \\ :empty) do
model
|> cast(params, ~w(country), ~w())
|> cast_by_country(params)
end
defp cast_by_country(changeset, params) do
case get_field(changeset, :country) do
"US" -> cast(changeset, params, ~w(zip), ~w())
_ -> cast(changeset, params, ~w(), ~w(zip))
end
end
get_field/2
will read a value from the changes and fallback to the struct one if there isn't one. This is the biggest benefit of changesets: it is just data structure and you can use regular Elixir code to do conditional checks, validations and so on. Straight-forward to write, read and test. :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With