Using inputs_for for a custom type instead of a `has` relation

I have some ecto model like this:

defmodule Rect do
  use Ecto.Schema

  alias __MODULE__

  embedded_schema do
    field(:width, :integer)
    field(:height, :integer)
  end
end

defmodule Circle do
  use Ecto.Schema

  alias __MODULE__

  embedded_schema do
    field(:radius, :float)
  end
end

defmodule Shape do
  use Ecto.Schema

  defmodule ShapeContent do
    @behaviour Ecto.Type

    def type, do: :map

    def cast(%Rect{} = shape), do: {:ok, shape}
    def cast(%Circle{} = shape), do: {:ok, shape}
    def cast(_), do: :error

    def load(%{"type" => "Rect"} = params) do
      case Rect.create(params) do
        {:ok, result} -> {:ok, result}
        {:error, _} -> :error
      end
    end
    def load(%{"type" => "Circle"} = params) do
      case Circle.create(params) do
        {:ok, result} -> {:ok, result}
        {:error, _} -> :error
      end
    end
    def load(_), do: :error

    def dump(map) when is_map(map), do: {:ok, map}
  end

  schema "shape" do
    field(:name, :string)
    field(:shape, ShapeContent)
  end
end

And now I want to create a page where a user can create a circle or a rect. In the case of a circle the user has to provide a name and radius. In case of rectangle, the user has to provide a name, width and height.

The example above is simplified, both the shape as the content shapes have more fields (both shared fields as none shared fields).

So my questions are:

  1. Is this the correct way to model my problem?
  2. How can I create a new_circle and new_rect page that show correct errors with form_for and changesets. I was looking into inputs_for, but what I probably want is a way to have 2 changesets in the form and submit them both?

Thanks

Could just do a @conn form and do it manually, or make a changeset that is just holding each of those two (don’t need a schema), or implement the form protocol for your own struct or so?

2 Likes

Thanks, didn’t think about the options to make a changeset without the schema. I’m doing something like:

shape_changeset = %{Shape.changeset(%Shape{}) | data: %{}}
circle_changeset = %{Circle.changeset(%Circle{}) | data: %{}}
Ecto.Changeset.merge(shape_changeset, circle_changeset)
1 Like