Ecto schema type based on another fields value?

Is it possible to define a schemas field type based on another field?

I have a table with a jsonb column called fields which is an array of what will be used as form fields which looks like this [{name: "name of field", type: "boolean", value: false}, {name: "second field", type: "string", value: "value goes here"}]

I’ve created an embedded schema for this but not sure how to define the type for value. Here’s what my schemas look like.

defmodule MyApp.Ads.Ad do
  use Ecto.Schema
  import Ecto.Changeset

  schema "ads" do
    field :name, :string
    embeds_many(:fields, MyApp.Ads.Field, on_replace: :delete)

    timestamps()
  end

  def changeset(ad, attrs) do
    ad
    |> cast(attrs, [:name])
    |> cast_embed(:fields)
    |> validate_required([:name, :fields])
  end
end

defmodule MyApp.Ads.Field do
  use Ecto.Schema
  import Ecto.Changeset

  embedded_schema do
    field :name, :string
    field :type, :string
    field :value, # this is based on the value of the type field
  end

  def changeset(field, attrs) do
    field
    |> cast(attrs, [:name, :type])
  end
end

While I don’t think its possible to define an ecto schema type based on another field I came up with a solution for this. I made a custom ecto type that supports all the types I want to support and then made a custom cast function that the changeset gets piped through to cast based on the other field.

You might find this useful: Polymorphic embeds for Ecto — Polymorphic Embed v1.9.0

2 Likes