Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to add a conditional required field?

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?

like image 534
Ben Cates Avatar asked Nov 25 '15 17:11

Ben Cates


People also ask

What is a conditionally required field?

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).


Video Answer


1 Answers

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. :)

like image 160
José Valim Avatar answered Nov 14 '22 00:11

José Valim