I've got a schema with two fields, a
and b
, and I want exactly one of the fields to be required. That is, if a
is provided b
should not be, and vice versa.
Is there a way to elegantly represent this with Ecto changeset validations? Something like this:
schema "foo" do
field(:a, :string)
field(:b, :string)
field(:c, :string)
timestamps()
end
def changeset(transaction, attrs) do
transaction
|> cast(attrs, [:a, :b, :c])
|> validate_required([:c])
|> validate_mutual_exclusion([:a, :b])
end
defp validate_mutual_exclusion(changeset, fields) do
# What goes here?
end
You can count the number of fields that are present and check if that equals 1:
defp validate_mutual_exclusion(changeset, fields) do
present = Enum.count(fields, fn field -> present?(get_field(changeset, field)) end)
case present do
1 -> changeset # ok
_ ->
# add an error to each field
Enum.reduce(fields, changeset, fn field, changeset ->
add_error(changeset, field, "exactly one of these must be present: #{inspect(fields)}")
end)
end
end
present?
just checks if the value is ""
or nil
:
def present?(nil), do: false
def present?(""), do: false
def present?(_), do: true